多线程多进程处理服务器并发(多进程处理如何解决僵死进程)

2024-03-13 20:44

本文主要是介绍多线程多进程处理服务器并发(多进程处理如何解决僵死进程),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.可循环发送数据的代码

2.改成循环之后每次发现只能处理一个客户端 

3.服务器端处理并发问题

3.1 思路

3.2 利用多线程实现并发

​编辑

3.3 利用多进程实现并发

3.3.1 多进程并发产生的僵死进程问题

​3.3.2 解决僵死进程问题


1.可循环发送数据的代码

服务器代码:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
int main()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字assert(sockfd!=-1);struct sockaddr_in saddr,caddr;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");//IP地址转换int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));assert(res!=-1);res=listen(sockfd,5);assert(res!=-1);while(1){int len=sizeof(saddr);printf("accept wait...\n");int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//链接套接字if(c<0){continue;}printf("accept c=%d\n",c);printf("accept client ip:%s ,port=%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));while(1){	char buff[128]={0};int n=recv(c,buff,127,0);//返回值为0说明断开连接if(n<=0){break;}printf("buff=%s\n",buff);send(c,"ok",2,0);}close(c);}close(sockfd);exit(0);
}

 客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
int main()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字assert(sockfd!=-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");int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));assert(res!=-1);while(1){printf("input:\n");char buff[128]={0};fgets(buff,127,stdin);if(strncmp(buff,"end",3)==0){break;}send(sockfd,buff,strlen(buff),0);memset(buff,0,128);recv(sockfd,buff,127,0);printf("read:%s\n",buff);}close(sockfd);exit(0);
}

运行结果:

2.改成循环之后每次发现只能处理一个客户端 

将代码从单词发送数据改为while(1)循环发送数据后,我们发现每次只能处理一个客户端,其它客户端消息无法发送给服务器。

原因: 

3.服务器端处理并发问题

3.1 思路

这个问题可以通过引入多线程和多进程来解决。

服务端接收一个客户端的连接后(accept之后),创建一个线程或者进程,然后在新创建的线程或进程中循环处理数据。

主线程(父进程)只负责监听客户端的连接,并使用 accept()接受连接,不进行数据的处理。如下图所示: 

3.2 利用多线程实现并发

客户端代码不变,服务器端代码做如下更改:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>
void* work_pthread(void*arg)
{	int c=*(int*)arg;while(1){char buff[128]={0};int n=recv(c,buff,127,0);//返回值为0说明断开连接if(n<=0){break;}printf("recv(%d)=%s\n",c,buff);send(c,"ok",2,0);}printf("one clinet over!\n");close(c);
}int main()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字assert(sockfd!=-1);struct sockaddr_in saddr,caddr;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");//IP地址转换int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));assert(res!=-1);res=listen(sockfd,5);assert(res!=-1);while(1){int len=sizeof(saddr);printf("accept wait...\n");int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//链接套接字if(c<0){continue;}printf("accept c=%d\n",c);printf("accept client ip:%s ,port=%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));pthread_t id;pthread_create(&id,NULL,work_pthread,(void*)&c);}close(sockfd);exit(0);
}

netstat -natp连接成功之后发现有两个./ser

3.3 利用多进程实现并发

客户端代码不变,服务器代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>void DealClientLink(int c,struct sockaddr_in caddr)
{while(1){char buff[128]={0};int n=recv(c,buff,127,0);//返回值为0说明断开连接if(n<=0){break;}printf("%s:%d:buff=%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buff);send(c,"ok",2,0);}printf("one clinet unlike!\n");close(c);
}int main()
{int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字assert(sockfd!=-1);struct sockaddr_in saddr,caddr;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");//IP地址转换int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));assert(res!=-1);res=listen(sockfd,5);assert(res!=-1);printf("%s:%d link success!\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));while(1){int len=sizeof(saddr);int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//链接套接字if(c<0){continue;}pid_t pid=fork();assert(pid!=-1);if(pid==0){DealClientLink(c,caddr);exit(0);}}close(sockfd);exit(0);
}

 运行结果:

3.3.1 多进程并发产生的僵死进程问题

子进程为客户端,父进程为服务器端,子进程先于父进程结束,父进程没有获取到子进程的退出码,子进程就会变成僵死进程,占用内存,影响执行速度。

客户端代码运行前:

关闭客户端(子进程结束)后:

如下图,产生了两个僵死进程。

 3.3.2 解决僵死进程问题

修改一下代码,让父进程调用wait()方法获取子进程的退出码,并结合信号使用,让它不再阻塞。

#include <wait.h>void fun(int sig)
{wait(&sig);
}signal(SIGCHLD,fun);//在主进程中添加

完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <wait.h>void DealClientLink(int c,struct sockaddr_in caddr)
{while(1){char buff[128]={0};int n=recv(c,buff,127,0);//返回值为0说明断开连接if(n<=0){break;}printf("%s:%d:buff=%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buff);send(c,"ok",2,0);}printf("one clinet unlike!\n");close(c);
}void fun(int sign)
{wait(&sign);
}int main()
{signal(SIGCHLD,fun);int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字assert(sockfd!=-1);struct sockaddr_in saddr,caddr;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");//IP地址转换int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));assert(res!=-1);res=listen(sockfd,5);assert(res!=-1);printf("%s:%d link success!\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));while(1){int len=sizeof(saddr);int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//链接套接字if(c<0){continue;}pid_t pid=fork();assert(pid!=-1);if(pid==0){DealClientLink(c,caddr);exit(0);}}close(sockfd);exit(0);
}

 使用ps -f命令查看进程信息,可以看到子进程退出后,没有僵死进程。

这篇关于多线程多进程处理服务器并发(多进程处理如何解决僵死进程)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/806147

相关文章

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

C++右移运算符的一个小坑及解决

《C++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

Python实现批量CSV转Excel的高性能处理方案

《Python实现批量CSV转Excel的高性能处理方案》在日常办公中,我们经常需要将CSV格式的数据转换为Excel文件,本文将介绍一个基于Python的高性能解决方案,感兴趣的小伙伴可以跟随小编一... 目录一、场景需求二、技术方案三、核心代码四、批量处理方案五、性能优化六、使用示例完整代码七、小结一、

Python中 try / except / else / finally 异常处理方法详解

《Python中try/except/else/finally异常处理方法详解》:本文主要介绍Python中try/except/else/finally异常处理方法的相关资料,涵... 目录1. 基本结构2. 各部分的作用tryexceptelsefinally3. 执行流程总结4. 常见用法(1)多个e

PHP应用中处理限流和API节流的最佳实践

《PHP应用中处理限流和API节流的最佳实践》限流和API节流对于确保Web应用程序的可靠性、安全性和可扩展性至关重要,本文将详细介绍PHP应用中处理限流和API节流的最佳实践,下面就来和小编一起学习... 目录限流的重要性在 php 中实施限流的最佳实践使用集中式存储进行状态管理(如 Redis)采用滑动

MyBatis-plus处理存储json数据过程

《MyBatis-plus处理存储json数据过程》文章介绍MyBatis-Plus3.4.21处理对象与集合的差异:对象可用内置Handler配合autoResultMap,集合需自定义处理器继承F... 目录1、如果是对象2、如果需要转换的是List集合总结对象和集合分两种情况处理,目前我用的MP的版本

504 Gateway Timeout网关超时的根源及完美解决方法

《504GatewayTimeout网关超时的根源及完美解决方法》在日常开发和运维过程中,504GatewayTimeout错误是常见的网络问题之一,尤其是在使用反向代理(如Nginx)或... 目录引言为什么会出现 504 错误?1. 探索 504 Gateway Timeout 错误的根源 1.1 后端

Web服务器-Nginx-高并发问题

《Web服务器-Nginx-高并发问题》Nginx通过事件驱动、I/O多路复用和异步非阻塞技术高效处理高并发,结合动静分离和限流策略,提升性能与稳定性... 目录前言一、架构1. 原生多进程架构2. 事件驱动模型3. IO多路复用4. 异步非阻塞 I/O5. Nginx高并发配置实战二、动静分离1. 职责2

解决升级JDK报错:module java.base does not“opens java.lang.reflect“to unnamed module问题

《解决升级JDK报错:modulejava.basedoesnot“opensjava.lang.reflect“tounnamedmodule问题》SpringBoot启动错误源于Jav... 目录问题描述原因分析解决方案总结问题描述启动sprintboot时报以下错误原因分析编程异js常是由Ja