网站推广公司 优帮云,灰色关键词排名代做,产品设计作品集欣赏,电商网站开发技术难点写一个服务器端用多进程处理并发#xff0c;使两个以上的客户端可以同时连接服务器端得到响应。每当接受一个新的连接就fork产生一个子进程#xff0c;让子进程去处理这个连接#xff0c;父进程只用来接受连接。
与多线程相比的不同点#xff1a;多线程如果其中一个线程操…写一个服务器端用多进程处理并发使两个以上的客户端可以同时连接服务器端得到响应。每当接受一个新的连接就fork产生一个子进程让子进程去处理这个连接父进程只用来接受连接。
与多线程相比的不同点多线程如果其中一个线程操作不当产生了一个信号会导致整个进程都终止。对于多进程来讲是产生的子进程去处理客户端的连接如果子进程终止了不会影响到其他进程。创建线程的开销比产生新的子进程的开销要小多线程可以共享整个进程的资源。
服务器端代码ser.c
#includestdio.h
#includestdlib.h
#includestring.h
#includeunistd.h
#includesys/socket.h
#includenetinet/in.h
#includearpa/inet.h
#includepthread.h
#includesignal.hint socket_init();//封装一个套接字套接字的声明
void do_run(int c)//子进程中处理客户端的方法的实现
{while(1){char buff[128]{0};int numrecv(c,buff,127,0);//相当于readif(num0){break;}printf(子进程接收到的信息:%s\n,buff);send(c,ok,2,0);//相当于write}
}int main()
{int sockfdsocket_init();//调用封装好的套接字方法创建套接字if(sockfd-1){printf(创建失败\n);exit(1);}while(1){struct sockaddr_in caddr;//定义客户端的套接字地址int len sizeof(caddr);//4.接收客户端连接int caccept(sockfd,(struct sockaddr*)caddr,len);if(c0)//失败{continue;}printf(c%d\n,c);//5.产生一个子进程来处理客户端的连接子进程会复制父进程打开的所有文件上面接收的客户端的连接c也算是文件描述符这个c也会被复制到子进程中pid_t pidfork();if(pid-1) {printf(子进程产生失败\n); close(c);//关闭c这个连接continue;//重新等待新的连接}else if(pid0)//子进程产生成功{close(sockfd);//因为子进程不用sockfddo_run(c);//处理客户端传入的参数是c这个描述符close(c);//子进程在退出之前关闭cprintf(子进程退出pid%d\n,getpid());exit(0);//处理完所连接的客户端子进程必须要退出}else {close(c);//父进程关闭c连接对子进程没有影响因为在fork之后描述符的引用计数加1这时候父进程关闭子进程也关闭最终才能认为c被关闭//如果父进程不关闭c连接c的值会一直增长}}exit(0);
}int socket_init()//套接字的具体实现
{int sockfdsocket(AF_INET,SOCK_STREAM,0);//1.创建套接字if(sockfd-1){printf(创建失败\n);return -1;}struct sockaddr_in saddr;//定义服务器端的套接字地址memset(saddr,0,sizeof(saddr));//清空saddr.sin_familyAF_INET;saddr.sin_porthtons(6000);saddr.sin_addr.s_addrinet_addr(127.0.0.1);//2.绑定指定套接字的ip和端口int resbind(sockfd,(struct sockaddr*)saddr,sizeof(saddr));if(res-1){printf(绑定失败\n);//ip写错或者端口被占用return -1;}//3.创建监听队列reslisten(sockfd,5);if(res-1){return -1;}return sockfd;}客户端代码cli.c
#includestdio.h
#includestdlib.h
#includestring.h
#includeunistd.h
#includesys/socket.h
#includenetinet/in.h
#includearpa/inet.hint main()
{//1.创建套接字int sockfdsocket(AF_INET,SOCK_STREAM,0);if(sockfd-1){exit(1);}//要连接服务器端就要知道服务器端的ip和端口把ip和端口存到下面定义的saddr中struct sockaddr_in saddr;memset(saddr,0,sizeof(saddr));//必须清空saddr.sin_familyAF_INET;saddr.sin_porthtons(6000);saddr.sin_addr.s_addrinet_addr(127.0.0.1);//2.连接服务器端int resconnect(sockfd,(struct sockaddr*)saddr,sizeof(saddr));//会随机填充客户端的ip和一个临时端口if(res-1)//网络没联通服务器端没启动{printf(连接服务器端失败\n);exit(1);}while(1){char buff[128]{0};printf(输入\n);fgets(buff,128,stdin);if(strncmp(buff,end,3)0){break;}//3.向服务器端发送消息send(sockfd,buff,strlen(buff),0);memset(buff,0,128);//4.接收服务器端回复的消息recv(sockfd,buff,127,0);printf(buff%s\n,buff);}//5.关闭客户端printf(客户端关闭\n);close(sockfd);
}运行结果 此时虽然两个客户端可以同时与服务器端通信但是存在一个问题就是僵死进程的问题如下图 之所以出现僵死进程是因为在子进程结束之后并没有处理子进程在Linux系统中处理僵死进程的方法有两种wait和忽略信号。
使用忽略信号的方式服务器端的代码ser.c如下
#includestdio.h
#includestdlib.h
#includestring.h
#includeunistd.h
#includesys/socket.h
#includenetinet/in.h
#includearpa/inet.h
#includepthread.h
#includesignal.hint socket_init();//封装一个套接字套接字的声明
void do_run(int c)//子进程中处理客户端的方法的实现
{while(1){char buff[128]{0};int numrecv(c,buff,127,0);//相当于readif(num0){break;}printf(子进程接收到的信息:%s\n,buff);send(c,ok,2,0);//相当于write}
}int main()
{signal(SIGCHLD,SIG_IGN);int sockfdsocket_init();//调用封装好的套接字方法创建套接字if(sockfd-1){printf(创建失败\n);exit(1);}while(1){struct sockaddr_in caddr;//定义客户端的套接字地址int len sizeof(caddr);//4.接收客户端连接int caccept(sockfd,(struct sockaddr*)caddr,len);if(c0)//失败{continue;}printf(c%d\n,c);//5.产生一个子进程来处理客户端的连接子进程会复制父进程打开的所有文件上面接收的客户端的连接c也算是文件描述符这个c也会被复制到子进程中pid_t pidfork();if(pid-1) {printf(子进程产生失败\n); close(c);//关闭c这个连接continue;//重新等待新的连接}else if(pid0)//子进程产生成功{close(sockfd);//因为子进程不用sockfddo_run(c);//处理客户端传入的参数是c这个描述符close(c);//子进程在退出之前关闭cprintf(子进程退出pid%d\n,getpid());exit(0);//处理完所连接的客户端子进程必须要退出}else {close(c);//父进程关闭c连接对子进程没有影响因为在fork之后描述符的引用计数加1这时候父进程关闭子进程也关闭最终才能认为c被关闭//如果父进程不关闭c连接c的值会一直增长}}exit(0);
}int socket_init()//套接字的具体实现
{int sockfdsocket(AF_INET,SOCK_STREAM,0);//1.创建套接字if(sockfd-1){printf(创建失败\n);return -1;}struct sockaddr_in saddr;//定义服务器端的套接字地址memset(saddr,0,sizeof(saddr));//清空saddr.sin_familyAF_INET;saddr.sin_porthtons(6000);saddr.sin_addr.s_addrinet_addr(127.0.0.1);//2.绑定指定套接字的ip和端口int resbind(sockfd,(struct sockaddr*)saddr,sizeof(saddr));if(res-1){printf(绑定失败\n);//ip写错或者端口被占用return -1;}//3.创建监听队列reslisten(sockfd,5);if(res-1){return -1;}return sockfd;}或者使用wait方法服务器端的代码ser.c如下
#includestdio.h
#includestdlib.h
#includestring.h
#includeunistd.h
#includesys/socket.h
#includenetinet/in.h
#includearpa/inet.h
#includepthread.h
#includesignal.hint socket_init();//封装一个套接字套接字的声明
void do_run(int c)//子进程中处理客户端的方法的实现
{while(1){char buff[128]{0};int numrecv(c,buff,127,0);//相当于readif(num0){break;}printf(子进程接收到的信息:%s\n,buff);send(c,ok,2,0);//相当于write}
}
void fun(int sig)
{wait(NULL);
}
int main()
{signal(SIGCHLD,fun);int sockfdsocket_init();//调用封装好的套接字方法创建套接字if(sockfd-1){printf(创建失败\n);exit(1);}while(1){struct sockaddr_in caddr;//定义客户端的套接字地址int len sizeof(caddr);//4.接收客户端连接int caccept(sockfd,(struct sockaddr*)caddr,len);if(c0)//失败{continue;}printf(c%d\n,c);//5.产生一个子进程来处理客户端的连接子进程会复制父进程打开的所有文件上面接收的客户端的连接c也算是文件描述符这个c也会被复制到子进程中pid_t pidfork();if(pid-1) {printf(子进程产生失败\n); close(c);//关闭c这个连接continue;//重新等待新的连接}else if(pid0)//子进程产生成功{close(sockfd);//因为子进程不用sockfddo_run(c);//处理客户端传入的参数是c这个描述符close(c);//子进程在退出之前关闭cprintf(子进程退出pid%d\n,getpid());exit(0);//处理完所连接的客户端子进程必须要退出}else {close(c);//父进程关闭c连接对子进程没有影响因为在fork之后描述符的引用计数加1这时候父进程关闭子进程也关闭最终才能认为c被关闭//如果父进程不关闭c连接c的值会一直增长}}exit(0);
}int socket_init()//套接字的具体实现
{int sockfdsocket(AF_INET,SOCK_STREAM,0);//1.创建套接字if(sockfd-1){printf(创建失败\n);return -1;}struct sockaddr_in saddr;//定义服务器端的套接字地址memset(saddr,0,sizeof(saddr));//清空saddr.sin_familyAF_INET;saddr.sin_porthtons(6000);saddr.sin_addr.s_addrinet_addr(127.0.0.1);//2.绑定指定套接字的ip和端口int resbind(sockfd,(struct sockaddr*)saddr,sizeof(saddr));if(res-1){printf(绑定失败\n);//ip写错或者端口被占用return -1;}//3.创建监听队列reslisten(sockfd,5);if(res-1){return -1;}return sockfd;}运行结果 这次僵死进程就不存在了。