Linux 多进程解决客户端与服务器端通信

写一个服务器端用多进程处理并发,使两个以上的客户端可以同时连接服务器端得到响应。每当接受一个新的连接就fork产生一个子进程,让子进程去处理这个连接,父进程只用来接受连接。

与多线程相比的不同点:多线程如果其中一个线程操作不当,产生了一个信号,会导致整个进程都终止。对于多进程来讲,是产生的子进程去处理客户端的连接,如果子进程终止了,不会影响到其他进程。创建线程的开销比产生新的子进程的开销要小,多线程可以共享整个进程的资源。

服务器端代码ser.c:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<signal.h>int socket_init();//封装一个套接字,套接字的声明
void do_run(int c)//子进程中处理客户端的方法的实现
{while(1){char buff[128]={0};int num=recv(c,buff,127,0);//相当于readif(num<=0){break;}printf("子进程接收到的信息:%s\n",buff);send(c,"ok",2,0);//相当于write}
}int main()
{int sockfd=socket_init();//调用封装好的套接字方法,创建套接字if(sockfd==-1){printf("创建失败\n");exit(1);}while(1){struct sockaddr_in caddr;//定义客户端的套接字地址int len = sizeof(caddr);//4.接收客户端连接int c=accept(sockfd,(struct sockaddr*)&caddr,&len);if(c<0)//失败{continue;}printf("c=%d\n",c);//5.产生一个子进程来处理客户端的连接,子进程会复制父进程打开的所有文件,上面接收的客户端的连接c也算是文件描述符,这个c也会被复制到子进程中pid_t pid=fork();if(pid==-1)  {printf("子进程产生失败\n"); close(c);//关闭c这个连接continue;//重新等待新的连接}else if(pid==0)//子进程产生成功{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 sockfd=socket(AF_INET,SOCK_STREAM,0);//1.创建套接字if(sockfd==-1){printf("创建失败\n");return -1;}struct sockaddr_in saddr;//定义服务器端的套接字地址memset(&saddr,0,sizeof(saddr));//清空saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//2.绑定,指定套接字的ip和端口int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("绑定失败\n");//ip写错或者端口被占用return -1;}//3.创建监听队列res=listen(sockfd,5);if(res==-1){return -1;}return sockfd;}

客户端代码cli.c:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>int main()
{//1.创建套接字int sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd==-1){exit(1);}//要连接服务器端就要知道服务器端的ip和端口,把ip和端口存到下面定义的saddr中struct sockaddr_in saddr;memset(&saddr,0,sizeof(saddr));//必须清空saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//2.连接服务器端int res=connect(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如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<signal.h>int socket_init();//封装一个套接字,套接字的声明
void do_run(int c)//子进程中处理客户端的方法的实现
{while(1){char buff[128]={0};int num=recv(c,buff,127,0);//相当于readif(num<=0){break;}printf("子进程接收到的信息:%s\n",buff);send(c,"ok",2,0);//相当于write}
}int main()
{signal(SIGCHLD,SIG_IGN);int sockfd=socket_init();//调用封装好的套接字方法,创建套接字if(sockfd==-1){printf("创建失败\n");exit(1);}while(1){struct sockaddr_in caddr;//定义客户端的套接字地址int len = sizeof(caddr);//4.接收客户端连接int c=accept(sockfd,(struct sockaddr*)&caddr,&len);if(c<0)//失败{continue;}printf("c=%d\n",c);//5.产生一个子进程来处理客户端的连接,子进程会复制父进程打开的所有文件,上面接收的客户端的连接c也算是文件描述符,这个c也会被复制到子进程中pid_t pid=fork();if(pid==-1)  {printf("子进程产生失败\n"); close(c);//关闭c这个连接continue;//重新等待新的连接}else if(pid==0)//子进程产生成功{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 sockfd=socket(AF_INET,SOCK_STREAM,0);//1.创建套接字if(sockfd==-1){printf("创建失败\n");return -1;}struct sockaddr_in saddr;//定义服务器端的套接字地址memset(&saddr,0,sizeof(saddr));//清空saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//2.绑定,指定套接字的ip和端口int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("绑定失败\n");//ip写错或者端口被占用return -1;}//3.创建监听队列res=listen(sockfd,5);if(res==-1){return -1;}return sockfd;}

或者使用wait方法,服务器端的代码ser.c如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<signal.h>int socket_init();//封装一个套接字,套接字的声明
void do_run(int c)//子进程中处理客户端的方法的实现
{while(1){char buff[128]={0};int num=recv(c,buff,127,0);//相当于readif(num<=0){break;}printf("子进程接收到的信息:%s\n",buff);send(c,"ok",2,0);//相当于write}
}
void fun(int sig)
{wait(NULL);
}
int main()
{signal(SIGCHLD,fun);int sockfd=socket_init();//调用封装好的套接字方法,创建套接字if(sockfd==-1){printf("创建失败\n");exit(1);}while(1){struct sockaddr_in caddr;//定义客户端的套接字地址int len = sizeof(caddr);//4.接收客户端连接int c=accept(sockfd,(struct sockaddr*)&caddr,&len);if(c<0)//失败{continue;}printf("c=%d\n",c);//5.产生一个子进程来处理客户端的连接,子进程会复制父进程打开的所有文件,上面接收的客户端的连接c也算是文件描述符,这个c也会被复制到子进程中pid_t pid=fork();if(pid==-1)  {printf("子进程产生失败\n"); close(c);//关闭c这个连接continue;//重新等待新的连接}else if(pid==0)//子进程产生成功{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 sockfd=socket(AF_INET,SOCK_STREAM,0);//1.创建套接字if(sockfd==-1){printf("创建失败\n");return -1;}struct sockaddr_in saddr;//定义服务器端的套接字地址memset(&saddr,0,sizeof(saddr));//清空saddr.sin_family=AF_INET;saddr.sin_port=htons(6000);saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//2.绑定,指定套接字的ip和端口int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res==-1){printf("绑定失败\n");//ip写错或者端口被占用return -1;}//3.创建监听队列res=listen(sockfd,5);if(res==-1){return -1;}return sockfd;}

运行结果:

在这里插入图片描述
在这里插入图片描述
这次僵死进程就不存在了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/94891.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中科驭数以DPU先进计算技术,夯实下一代金融IT基础设施底座

由中国计算机学会主办的第19届CCF全国高性能计算学术年会&#xff08;CCF HPC China 2023&#xff09;于8月23日至26日在青岛成功召开。在“高性能金融计算”主题论坛上&#xff0c;中科驭数高级副总裁、CTO卢文岩应邀发表了题为《DPU先进计算技术助力下一代交易底座》的演讲&a…

漏洞修复:在应用程序中发现不必要的 Http 响应头

描述 blablabla描述&#xff0c;一般是在返回的响应表头中出现了Server键值对&#xff0c;那我们要做的就是移除它&#xff0c;解决方案中提供了nginx的解决方案 解决方案 第一种解决方案 当前解决方案会隐藏nginx的版本号&#xff0c;但还是会返回nginx字样&#xff0c;如…

Vue3实现可视化拖拽标签小程序

介绍 实现功能&#xff1a;可视化标签拖拽&#xff0c;双击标签可修改标签内容 HTML结构 <div class"box" v-move><div class"header">标签1</div><div dblclick"startEditing" v-if"!isEditing">{{content…

windows 2012服务器配置nginx后无法访问域名的问题

环境&#xff1a;Windows 2012 R2 Nginx 问题&#xff1a;确认域名解析到服务器ip已生效&#xff08;通过ping域名地址确认域名已指向该ip&#xff09;&#xff0c;确认nginx配置无误&#xff08;绑定域名、配置端口、配置网站文件目录&#xff09;&#xff0c;但无法从外网访…

自动化运维:Ansible之playbook基于ROLES部署LNMP平台

目录 一、理论 1.playbook剧本 2.ROLES角色 3.关系 4.Roles模块搭建LNMP架构 二、实验 1.Roles模块搭建LNMP架构 三、问题 1.剧本启动php报错语法问题 2.剧本启动mysql报错语法问题 3.剧本启动nginx开启失败 4.剧本安装php失败 5.使用yum时报错 6.rpm -Uvh https…

A 股个股资金流 API 数据接口

A 股个股资金流 API 数据接口 全量股票资金流数据&#xff0c;全量A股数据&#xff0c;最长30日历史数据 1. 产品功能 支持所有A股资金流数据查询&#xff1b;每日定时更新数据&#xff1b;支持多达 30 日历史数据查询&#xff1b;超高的查询效率&#xff0c;数据秒级返回&am…

MongoDB实验——在MongoDB集合中查找文档

在MongoDB集合中查找文档 一、实验目的二、实验原理三、实验步骤1.启动MongoDB数据库、启动MongoDB Shell客户端2.数据准备-->person.json3.指定返回的键4 .包含或不包含 i n 或 in 或 in或nin、$elemMatch&#xff08;匹配数组&#xff09;5.OR 查询 $or6.Null、$exists7.…

Windows7安装SSH客户端的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

【数据结构】2015统考真题 6

题目描述 【2015统考真题】求下面的带权图的最小&#xff08;代价&#xff09;生成树时&#xff0c;可能是Kruskal算法第2次选中但不是Prim算法&#xff08;从v4开始&#xff09;第2次选中的边是&#xff08;C&#xff09; A. (V1, V3) B. (V1, V4) C. (V2, V3) D. (V3, V4) …

ToBeWritten之VSOC安全运营

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 转移发布平台通知&#xff1a;将不再在CSDN博客发布新文章&#xff0c;敬…

华为OD机试 - 敏感字段加密(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#xff09;》…

微软表示Visual Studio的IDE即日起开启“退休”倒计时

据了解&#xff0c;日前有消息透露称&#xff0c;适用于 Mac平台的Visual Studio集成开发环境(IDE)于8月31日启动“退休”进程。 而这意味着Visual Studio for Mac 17.6将继续支持12个月&#xff0c;一直到2024年8月31日。    微软表示后续不再为Visual Studio for Mac开发…