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


    目录

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

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

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

    3.1 思路

    3.2 利用多线程实现并发

    ​编辑

    3.3 利用多进程实现并发

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

    ​3.3.2 解决僵死进程问题


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

    服务器代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. int main()
    10. {
    11. int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字
    12. assert(sockfd!=-1);
    13. struct sockaddr_in saddr,caddr;
    14. memset(&saddr,0,sizeof(saddr));
    15. saddr.sin_family=AF_INET;
    16. saddr.sin_port=htons(6000);//主机,网络大小端转换
    17. saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//IP地址转换
    18. int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    19. assert(res!=-1);
    20. res=listen(sockfd,5);
    21. assert(res!=-1);
    22. while(1)
    23. {
    24. int len=sizeof(saddr);
    25. printf("accept wait...\n");
    26. int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//链接套接字
    27. if(c<0)
    28. {
    29. continue;
    30. }
    31. printf("accept c=%d\n",c);
    32. printf("accept client ip:%s ,port=%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
    33. while(1)
    34. {
    35. char buff[128]={0};
    36. int n=recv(c,buff,127,0);//返回值为0说明断开连接
    37. if(n<=0)
    38. {
    39. break;
    40. }
    41. printf("buff=%s\n",buff);
    42. send(c,"ok",2,0);
    43. }
    44. close(c);
    45. }
    46. close(sockfd);
    47. exit(0);
    48. }

     客户端代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. int main()
    10. {
    11. int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字
    12. assert(sockfd!=-1);
    13. struct sockaddr_in saddr;
    14. memset(&saddr,0,sizeof(saddr));
    15. saddr.sin_family=AF_INET;
    16. saddr.sin_port=htons(6000);
    17. saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    18. int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    19. assert(res!=-1);
    20. while(1)
    21. {
    22. printf("input:\n");
    23. char buff[128]={0};
    24. fgets(buff,127,stdin);
    25. if(strncmp(buff,"end",3)==0)
    26. {
    27. break;
    28. }
    29. send(sockfd,buff,strlen(buff),0);
    30. memset(buff,0,128);
    31. recv(sockfd,buff,127,0);
    32. printf("read:%s\n",buff);
    33. }
    34. close(sockfd);
    35. exit(0);
    36. }

    运行结果:

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

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

    原因: 

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

    3.1 思路

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

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

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

    3.2 利用多线程实现并发

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

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. void* work_pthread(void*arg)
    11. {
    12. int c=*(int*)arg;
    13. while(1)
    14. {
    15. char buff[128]={0};
    16. int n=recv(c,buff,127,0);//返回值为0说明断开连接
    17. if(n<=0)
    18. {
    19. break;
    20. }
    21. printf("recv(%d)=%s\n",c,buff);
    22. send(c,"ok",2,0);
    23. }
    24. printf("one clinet over!\n");
    25. close(c);
    26. }
    27. int main()
    28. {
    29. int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字
    30. assert(sockfd!=-1);
    31. struct sockaddr_in saddr,caddr;
    32. memset(&saddr,0,sizeof(saddr));
    33. saddr.sin_family=AF_INET;
    34. saddr.sin_port=htons(6000);//主机,网络大小端转换
    35. saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//IP地址转换
    36. int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    37. assert(res!=-1);
    38. res=listen(sockfd,5);
    39. assert(res!=-1);
    40. while(1)
    41. {
    42. int len=sizeof(saddr);
    43. printf("accept wait...\n");
    44. int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//链接套接字
    45. if(c<0)
    46. {
    47. continue;
    48. }
    49. printf("accept c=%d\n",c);
    50. printf("accept client ip:%s ,port=%d\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
    51. pthread_t id;
    52. pthread_create(&id,NULL,work_pthread,(void*)&c);
    53. }
    54. close(sockfd);
    55. exit(0);
    56. }

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

    3.3 利用多进程实现并发

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

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. void DealClientLink(int c,struct sockaddr_in caddr)
    10. {
    11. while(1)
    12. {
    13. char buff[128]={0};
    14. int n=recv(c,buff,127,0);//返回值为0说明断开连接
    15. if(n<=0)
    16. {
    17. break;
    18. }
    19. printf("%s:%d:buff=%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buff);
    20. send(c,"ok",2,0);
    21. }
    22. printf("one clinet unlike!\n");
    23. close(c);
    24. }
    25. int main()
    26. {
    27. int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字
    28. assert(sockfd!=-1);
    29. struct sockaddr_in saddr,caddr;
    30. memset(&saddr,0,sizeof(saddr));
    31. saddr.sin_family=AF_INET;
    32. saddr.sin_port=htons(6000);//主机,网络大小端转换
    33. saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//IP地址转换
    34. int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    35. assert(res!=-1);
    36. res=listen(sockfd,5);
    37. assert(res!=-1);
    38. printf("%s:%d link success!\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
    39. while(1)
    40. {
    41. int len=sizeof(saddr);
    42. int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//链接套接字
    43. if(c<0)
    44. {
    45. continue;
    46. }
    47. pid_t pid=fork();
    48. assert(pid!=-1);
    49. if(pid==0)
    50. {
    51. DealClientLink(c,caddr);
    52. exit(0);
    53. }
    54. }
    55. close(sockfd);
    56. exit(0);
    57. }

     运行结果:

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

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

    客户端代码运行前:

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

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

     3.3.2 解决僵死进程问题

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

    1. #include
    2. void fun(int sig)
    3. {
    4. wait(&sig);
    5. }
    6. signal(SIGCHLD,fun);//在主进程中添加

    完整代码:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. void DealClientLink(int c,struct sockaddr_in caddr)
    11. {
    12. while(1)
    13. {
    14. char buff[128]={0};
    15. int n=recv(c,buff,127,0);//返回值为0说明断开连接
    16. if(n<=0)
    17. {
    18. break;
    19. }
    20. printf("%s:%d:buff=%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buff);
    21. send(c,"ok",2,0);
    22. }
    23. printf("one clinet unlike!\n");
    24. close(c);
    25. }
    26. void fun(int sign)
    27. {
    28. wait(&sign);
    29. }
    30. int main()
    31. {
    32. signal(SIGCHLD,fun);
    33. int sockfd=socket(AF_INET,SOCK_STREAM,0);//监听套接字
    34. assert(sockfd!=-1);
    35. struct sockaddr_in saddr,caddr;
    36. memset(&saddr,0,sizeof(saddr));
    37. saddr.sin_family=AF_INET;
    38. saddr.sin_port=htons(6000);//主机,网络大小端转换
    39. saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//IP地址转换
    40. int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    41. assert(res!=-1);
    42. res=listen(sockfd,5);
    43. assert(res!=-1);
    44. printf("%s:%d link success!\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
    45. while(1)
    46. {
    47. int len=sizeof(saddr);
    48. int c=accept(sockfd,(struct sockaddr*)&caddr,&len);//链接套接字
    49. if(c<0)
    50. {
    51. continue;
    52. }
    53. pid_t pid=fork();
    54. assert(pid!=-1);
    55. if(pid==0)
    56. {
    57. DealClientLink(c,caddr);
    58. exit(0);
    59. }
    60. }
    61. close(sockfd);
    62. exit(0);
    63. }

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

  • 相关阅读:
    Java 密码规则验证Passay 库
    解决 uni-app app使用 onBackPress 监听返回后关闭popup 以及使用 uni-simple-router 有冲突问题
    java算法day5
    springcloud第4季 使用resilience4j实现服务流量治理
    BSV 上用于通用计算的隐私非交互式赏金
    Linux开发讲课18--- “>file 2>&1“ 和 “2>&1 >file“ 的区别
    2010年5月27日Go生态洞察:I/O中Go的热门问答
    [数据结构]链表OJ题 (二) 反转链表
    简单阐述函数
    LC打怪录 选择排序 215.Kth Largest Element in an Array
  • 原文地址:https://blog.csdn.net/m0_62689947/article/details/136588958