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.rhkb.cn/news/122145.html

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

相关文章

小红书笔记爬虫

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

Ubuntu入门05——磁盘管理与备份压缩

1.检查磁盘空间占用情况 2.统计目录或文件所占磁盘空间大小 3.压缩 3.1 zip、unzip和zipinfo 运行时发现上面命令不成功&#xff0c;换成&#xff1a; &#xff08;将文件lkw放入压缩文件lkw01.zip中&#xff09; sudo zip -m lkw01.zip lkw 解压文件&#xff1a; 实操&…

MySQL从入门到精通【实践篇】之使用Sharding-JDBC 分库分表详解

文章目录 0. 前言本文技术组件版本基本介绍 2. 使用和配置&#xff1a;步骤1 引入依赖步骤2 配置数据源和分片策略步骤3 核心代码MybatisPlusConfig 核心配置OrderServiceOrderServiceImplOrderInfoOrderMapperOrderControllerBaseMapper 3. 数据库分片配置在我的demo工程中大家…

高等数学刷题

研究可不可导的两大问题 1.▲x能不能->0(正负均要) 2.函数可不可以不连续&#xff0c;通常用第二个例子 隐藏条件的挖掘 关键在于分析出f(x0)0f(0)

Kubernetes技术--k8s核心技术 configMap

1.概述 configMap最主要的作用是存储一些不加密的数据到/etcd,让pod以变量或者数据卷(volume)挂载到容器。 应用场景:配置文件、存储信息等 2.使用 -1.创建配置文件。 这里我们需要先编写一个配置文件。使用redis,如下所示:

postgresql-类型转换函数

postgresql-类型转换函数 简介CAST 函数to_date函数to_timestampto_charto_number隐式类型转换 简介 类型转换函数用于将数据从一种类型转换为另一种类型。 CAST 函数 CAST ( expr AS data_type )函数用于将 expr 转换为 data_type 数据类型&#xff1b;PostgreSQL 类型转 换…

axios返回几种数据格式? 其中Blob返回时的size是什么意思?

axios返回几种数据格式? 其中Blob返回时的size是什么意思&#xff1f; 1、字符串&#xff08;String&#xff09;&#xff1a;服务器可以返回纯文本或HTML内容&#xff0c;Axios会将其作为字符串返回。 2、JSON&#xff08;JavaScript Object Notation&#xff09;&#xff…

Matlab信号处理1:模拟去除信号噪声

由于工作内容涉及信号系统、信号处理相关知识&#xff0c;本人本硕均为计算机相关专业&#xff0c;专业、研究方向均未涉及信号相关知识&#xff0c;因此需进行系统地学习。之前已将《信号与系统》快速过了一遍&#xff0c;但感觉较抽象且理解较浅显。在此系统地学习如何使用Ma…

RK3568平台开发系列讲解(音视频篇)H264 的编码结构

🚀返回专栏总目录 文章目录 一、H264 的编码结构1.1、帧类型1.2、GOP1.3、Slice沉淀、分享、成长,让自己和他人都能有所收获!😄 📢视频编码的码流结构其实就是指视频经过编码之后得到的二进制数据是怎么组织的,换句话说,就是编码后的码流我们怎么将一帧帧编码后的图像…

vue优化首屏加载时间优化-cdn引入第三方包

前言 为什么要进行首屏加载优化&#xff0c;因为随着我们静态资源和第三方包和代码增加&#xff0c;压缩之后包会越来越大 随着网络的影响&#xff0c;在我们第一输入url请求资源时候&#xff0c;网络阻塞&#xff0c;加载时间长&#xff0c;用户体验不好 仔细观察后就会发现…

肖sir__设计测试用例方法之判定表06_(黑盒测试)

设计测试用例方法之判定表 1、判定表&#xff1a;是一种表达逻辑判断的工具。 2、判定表&#xff1a;包含四部分 1&#xff09;条件桩&#xff08;condition stub&#xff09;:列出问题的 所有条件&#xff08;通常条件次序无关紧要&#xff09;。 2&#xff09;条件项&#x…

zabbix监控实战

1 zabbix监控平台部署 更改的密码为HLThlt123 创建zabbix数据库 配置zabbix server 配置zabbix前端 启动服务 访问 更改成中文 修改字体文件 2 zabbix添加监控节点 在客户端 在server端 手动添加 自动添加 03 zabbix api 自动注册 停掉自动发现 删掉server3 自动注册 3 zab…

Springboot - 13.spring-boot-starter-security集成

&#x1f440;Spring Boot Starter Security 中文文档 Spring Security中文文档 &#x1f440;Spring Boot Starter Security 运行流程 当然可以。首先&#xff0c;我们会将用户存储和认证的流程融入整个Spring Boot Starter Security的使用流程中。以下是当你使用Spring Bo…

01_TMS320F28004x系列MCU介绍和资料搜集

1. TI C2000 实时微控制器 TI公司在处理器方面的产品线有&#xff1a;基于ARM内核的微控制器/微处理器、MSP430微控制器、C2000系列实时微控制器、还有数字信号处理器&#xff08;DSP&#xff09;。 其中&#xff0c;C2000是TI公司专门针对实时控制推出的32位微控制器。TI公司…

Java复习-20-接口(1)

接口的定义及使用 如果相对外部隐藏全部的实现细节&#xff0c;就要通过接口来实现。 接口的定义 使用interface关键字来定义。由于接口描述的是一个公共的定义标准&#xff0c;所以在接口之中所有的抽象方法的访问权限都为public interface IMessage{ // 为了区分接口&…

Cyber RT学习笔记 --- 1.Cyber RT框架介绍

1.Cyber RT框架介绍 1.1 Cyber RT简介 Apollo Cyber是首个专为自动驾驶定制的高性能且开源的实时通信框架&#xff0c;于2019年与Apollo 3.5开放平台同期发布&#xff0c;它主要解决了自动驾驶系统的高并发、低延迟、高吞吐、任务调度等问题&#xff0c;同时还提供了多种通信…

Gof23设计模式之策略模式

1.概述 该模式定义了一系列算法&#xff0c;并将每个算法封装起来&#xff0c;使它们可以相互替换&#xff0c;且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式&#xff0c;它通过对算法进行封装&#xff0c;把使用算法的责任和算法的实现分割开来&#xff0c;…

Xilinx UltraScale架构之可配置逻辑块CLB

目录 一、概览 二、UltraScale架构 2.1 UltraScale/UltraScale特点 2.2 与7系列CLB差异 三、 CLB结构 3.1 LUT 3.2 FF 3.3 多路选择器Multiplexers 3.4 进位链Carry Chain 四、应用 4.1 分布式RAM 4.2 移位寄存器 4.3 进位链Carry Chain 五、参考资料 一、概览 二…

Linux--进程概念

1.什么是程序&#xff1f;什么是进程&#xff1f;有什么区别&#xff1f; 程序&#xff1a;是静态的概念&#xff0c;gcc xxx.c -o pro 磁盘中生成的pro文件&#xff0c;叫做程序。 进程&#xff1a;是程序的一种与运行活动&#xff0c;通俗的意思是程序跑起来了&#xff0c;系…

若依tab-content面板失效、使用load的解决方法(附详细步骤)

【版权所有&#xff0c;文章允许转载&#xff0c;但须以链接方式注明源地址&#xff0c;否则追究法律责任】【创作不易&#xff0c;点个赞就是对我最大的支持】 前言 仅作为学习笔记&#xff0c;供大家参考 总结的不错的话&#xff0c;记得点赞收藏关注哦&#xff01; 思路&…