网络编程:实现多进程并发回声服务器端/客户端
1.直接跳转到Linux端代码
一、实验目的
- 学习进程的创建、销毁过程。掌握利用信号处理技术sigaction消灭僵尸进程的方法。
- 在Linux操作系统上编写并发服务器端/客户端。让服务器端以多进程方式为多个客户端同时提供回声服务。
二、实验内容
1、在Linux操作系统上编写程序,实现基于多进程的并发回声服务器端:
(1)改进实验三中实现的迭代服务器端程序。通过为每一个客户端创建进程的方式,同时为多个客户端提供回声服务。
(2)客户端接收用户输入的字符串并发送到服务器端,一直到用户输入字符 ”Q”/”q” 为止。
(3)启动服务器后创建两个以上客户端并建立连接,验证服务器端可以同时为不同的客户端提供回声服务。
2、回声客户端采用I/O程序分割方法,发送的消息记录到文件中
实验结果
Linux端效果图如下:
Linux端的(采用UOS+VScode+g++)
Linux端代码如下:
1. 服务器端:
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include<fstream>
#include <signal.h>
#include <sys/wait.h>
#pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll
#define BUF_SIZE 1024
using namespace std;
void read_childproc(int sig);
int main(){
pid_t pid;
struct sigaction act;
int strlen;
//配置信号处理函数
act.sa_handler=read_childproc;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
sigaction(SIGCHLD, &act, 0);
//创建套接字
int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//将套接字和IP、端口绑定
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充
serv_addr.sin_family = AF_INET; //使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址
serv_addr.sin_port = htons(1234); //端口
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//进入监听状态,等待用户发起请求
listen(serv_sock, 20);
//接收客户端请求
char buffer[BUF_SIZE] = {0}; //缓冲区
for(int i=0;;i++){
struct sockaddr_in clnt_addr;
socklen_t clnt_addr_size = sizeof(clnt_addr);
int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
if (clnt_sock == -1)
cout<<"某一客户端断开"<<endl;
else
cout<<"已连接客户端:"<<i + 1<<endl;
pid=fork(); //创建新进程
if(pid==-1)
{
close(clnt_sock);
continue;
}
if(pid==0) //子进程运行区域
{
close(serv_sock); //在子进程中要关闭服务器套接字
while((strlen = read(clnt_sock, buffer, sizeof(buffer)))!=0){ //接收客户端发来的数据
write(clnt_sock,buffer,strlen); //将数据原样返回
FILE *fp = fopen("result.txt","a");//客户端传过来的数据写入文件
fputs(buffer,fp);//客户端传过来的数据写入文件
fclose(fp);//关闭文件
}
close(clnt_sock);//关闭套接字
cout<<"客户端断开"<<endl;
return 0;
}
else //父进程运行区域
{
//调用fork函数后,要将无关的套接字文件描述符关闭掉
close(clnt_sock);
}
// memset(buffer, 0, BUF_SIZE); //重置缓冲区
}
//关闭套接字
close(serv_sock);
return 0;
}
//一旦有子进程结束就调用这个函数
void read_childproc(int sig)
{
int status;
pid_t id=waitpid(-1, &status, WNOHANG);
if(WIFEXITED(status))
{
cout<<"结束进程,id: "<<id<<endl;
cout<<"子进程发送: "<<WEXITSTATUS(status)<<endl;
}
}
//g++ 网络编程作业5服务器端.cpp -o test1
2. 客户端:
#include <iostream>
#include <cstring>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUFFER_SIZE 1024
void read_routine(int sock, char *buf);
void write_routine(int sock, char *buf);
using namespace std;
int main(){
//设置套接字相关属性
pid_t pid;
struct sockaddr_in serv_addr;
struct sockaddr_in sock_addr;
memset(&sock_addr,0,sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
sock_addr.sin_port = htons(1234);
//发送缓冲区和接收缓冲区
char bufSend[BUFFER_SIZE];
int result;
int num;
//创建套接字
int sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
connect(sock,(struct sockaddr*)&sock_addr,sizeof(sock_addr));
pid=fork();
if(pid==0) //子进程写
write_routine(sock, bufSend);
else //父进程读
read_routine(sock, bufSend);
}
void read_routine(int sock, char* buf)
{
while(1)
{
int str_len=read(sock, buf, BUFFER_SIZE);
if(str_len==0)
return; //接受到EOF结束符时返回
buf[str_len]=0;
cout<<"message from server: "<<buf<<endl;
}
}
void write_routine(int sock, char* buf)
{
while(1)
{
fgets(buf, BUFFER_SIZE, stdin);
if(!strcmp(buf, "q\n") || !strcmp(buf, "Q\n"))
{
shutdown(sock, SHUT_WR);
return;
}
write(sock, buf, strlen(buf));
}
}
//g++ 网络编程作业5客户端.cpp -o test2