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

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

相关文章

如何使用celery进行异步处理和定时任务(django)

《如何使用celery进行异步处理和定时任务(django)》文章介绍了Celery的基本概念、安装方法、如何使用Celery进行异步任务处理以及如何设置定时任务,通过Celery,可以在Web应用中... 目录一、celery的作用二、安装celery三、使用celery 异步执行任务四、使用celery

oracle数据库索引失效的问题及解决

《oracle数据库索引失效的问题及解决》本文总结了在Oracle数据库中索引失效的一些常见场景,包括使用isnull、isnotnull、!=、、、函数处理、like前置%查询以及范围索引和等值索引... 目录oracle数据库索引失效问题场景环境索引失效情况及验证结论一结论二结论三结论四结论五总结ora

element-ui下拉输入框+resetFields无法回显的问题解决

《element-ui下拉输入框+resetFields无法回显的问题解决》本文主要介绍了在使用ElementUI的下拉输入框时,点击重置按钮后输入框无法回显数据的问题,具有一定的参考价值,感兴趣的... 目录描述原因问题重现解决方案方法一方法二总结描述第一次进入页面,不做任何操作,点击重置按钮,再进行下

SpringBoot操作spark处理hdfs文件的操作方法

《SpringBoot操作spark处理hdfs文件的操作方法》本文介绍了如何使用SpringBoot操作Spark处理HDFS文件,包括导入依赖、配置Spark信息、编写Controller和Ser... 目录SpringBoot操作spark处理hdfs文件1、导入依赖2、配置spark信息3、cont

Apache Tomcat服务器版本号隐藏的几种方法

《ApacheTomcat服务器版本号隐藏的几种方法》本文主要介绍了ApacheTomcat服务器版本号隐藏的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需... 目录1. 隐藏HTTP响应头中的Server信息编辑 server.XML 文件2. 修China编程改错误

解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题

《解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题》本文主要讲述了在使用MyBatis和MyBatis-Plus时遇到的绑定异常... 目录myBATis-plus-boot-starpythonter与mybatis-spring-b

如何在一台服务器上使用docker运行kafka集群

《如何在一台服务器上使用docker运行kafka集群》文章详细介绍了如何在一台服务器上使用Docker运行Kafka集群,包括拉取镜像、创建网络、启动Kafka容器、检查运行状态、编写启动和关闭脚本... 目录1.拉取镜像2.创建集群之间通信的网络3.将zookeeper加入到网络中4.启动kafka集群

Python如何实现 HTTP echo 服务器

《Python如何实现HTTPecho服务器》本文介绍了如何使用Python实现一个简单的HTTPecho服务器,该服务器支持GET和POST请求,并返回JSON格式的响应,GET请求返回请求路... 一个用来做测试的简单的 HTTP echo 服务器。from http.server import HT

电脑显示hdmi无信号怎么办? 电脑显示器无信号的终极解决指南

《电脑显示hdmi无信号怎么办?电脑显示器无信号的终极解决指南》HDMI无信号的问题却让人头疼不已,遇到这种情况该怎么办?针对这种情况,我们可以采取一系列步骤来逐一排查并解决问题,以下是详细的方法... 无论你是试图为笔记本电脑设置多个显示器还是使用外部显示器,都可能会弹出“无HDMI信号”错误。此消息可能

mysql主从及遇到的问题解决

《mysql主从及遇到的问题解决》本文详细介绍了如何使用Docker配置MySQL主从复制,首先创建了两个文件夹并分别配置了`my.cnf`文件,通过执行脚本启动容器并配置好主从关系,文中还提到了一些... 目录mysql主从及遇到问题解决遇到的问题说明总结mysql主从及遇到问题解决1.基于mysql