当前位置:   article > 正文

Linux网络编程---多进/线程并发服务器

Linux网络编程---多进/线程并发服务器

一、多进程并发服务器

实现一个服务器可以连接多个客户端,每当accept函数等待到客户端进行连接时 就创建一个子进程

思路分析:

核心思路:让accept循环阻塞等待客户端,每当有客户端连接时就fork子进程,让子进程去和客户端进行通信,父进程用于监听并使用信号捕捉回收子进程(子进程关闭用于监听的套接字lfd,父进程关闭用于通信的cfd) 

服务端server.c代码如下:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<arpa/inet.h>
  4. #include<ctype.h>
  5. #include<sys/socket.h>
  6. #include<unistd.h>
  7. #include<string.h>
  8. #include<strings.h>
  9. #include<errno.h>
  10. #include<pthread.h>
  11. #include<signal.h>
  12. #include<sys/wait.h>
  13. #define SERV_PORT 9527
  14. void sys_err(const char *str)
  15. {
  16. perror(str);
  17. exit(1);
  18. }
  19. void catch_child(int signum)
  20. {
  21. while(waitpid(0,NULL,WNOHANG) > 0);
  22. return ;
  23. }
  24. int main(int argc,char *argv[])
  25. {
  26. int lfd = 0,cfd = 0;
  27. pid_t pid;
  28. int ret;
  29. char buf[BUFSIZ],client_IP[1024];//4096
  30. struct sockaddr_in serv_addr,clit_addr;
  31. socklen_t clit_addr_len;
  32. bzero(&serv_addr,sizeof(serv_addr));//将地址结构清零
  33. serv_addr.sin_family = AF_INET;
  34. serv_addr.sin_port = htons(SERV_PORT);
  35. serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  36. lfd = socket(AF_INET,SOCK_STREAM,0);
  37. if (lfd == -1)
  38. {
  39. sys_err("socket errno");
  40. }
  41. bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
  42. listen(lfd,128);
  43. clit_addr_len = sizeof(clit_addr);
  44. while(1)
  45. {
  46. cfd = accept(lfd,(struct sockaddr *)&clit_addr,&clit_addr_len);
  47. if (cfd == -1)
  48. {
  49. sys_err("accept error");
  50. }
  51. printf("client ip is:%s port:%d\n",inet_ntop(AF_INET,&clit_addr.sin_addr.s_addr,client_IP,sizeof(client_IP)),ntohs(clit_addr.sin_port));
  52. pid = fork();
  53. if (pid < 0)
  54. {
  55. sys_err("fork error");
  56. }
  57. else if (pid == 0)
  58. {
  59. close(lfd);
  60. break;
  61. }
  62. else
  63. {
  64. struct sigaction act;
  65. act.sa_handler = catch_child;
  66. sigemptyset(&(act.sa_mask));
  67. act.sa_flags = 0;
  68. ret = sigaction(SIGCHLD,&act,NULL);
  69. if (ret != 0)
  70. {
  71. sys_err("sigaction error");
  72. }
  73. close(cfd);
  74. continue;
  75. }
  76. }
  77. if (pid == 0)
  78. {
  79. for(;;)
  80. {
  81. ret = read(cfd,buf,sizeof(buf));
  82. if (ret == 0)
  83. {
  84. close(cfd);
  85. exit(1);
  86. }
  87. for (int i = 0;i<ret;i++)
  88. {
  89. buf[i] = toupper(buf[i]);
  90. }
  91. write(cfd,buf,ret);
  92. write(STDOUT_FILENO,buf,ret);
  93. }
  94. }
  95. return 0;
  96. }

一个服务端三个客户端执行多进程服务器并发输出如下: 

补充:

二、多线程并发服务器: 

思路分析:

核心思路:设置监听循环由主线程阻塞等待客户端连接,每当接受到新的客户端连接时,主线程会创建一个新的子线程来处理与该客户端的通信,而主线程继续执行监听循环等待其他客户端(主线程专注于接受新连接,子线程专注于处理客户端请求)

 服务端server.c代码如下:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <arpa/inet.h>
  4. #include <pthread.h>
  5. #include <ctype.h>
  6. #include <unistd.h>
  7. #include <fcntl.h>
  8. #include "wrap.h"
  9. #define MAXLINE 8192
  10. #define SERV_PORT 8000
  11. struct s_info { //定义一个结构体, 将地址结构跟cfd捆绑
  12. struct sockaddr_in cliaddr;
  13. int connfd;
  14. };
  15. void *do_work(void *arg)
  16. {
  17. int n,i;
  18. struct s_info *ts = (struct s_info*)arg;
  19. char buf[MAXLINE];
  20. char str[INET_ADDRSTRLEN]; //#define INET_ADDRSTRLEN 16 可用"[+d"查看
  21. while (1) {
  22. n = Read(ts->connfd, buf, MAXLINE); //读客户端
  23. if (n == 0) {
  24. printf("the client %d closed...\n", ts->connfd);
  25. break; //跳出循环,关闭cfd
  26. }
  27. printf("received from %s at PORT %d\n",
  28. inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),
  29. ntohs((*ts).cliaddr.sin_port)); //打印客户端信息(IP/PORT)
  30. for (i = 0; i < n; i++)
  31. buf[i] = toupper(buf[i]); //小写-->大写
  32. Write(STDOUT_FILENO, buf, n); //写出至屏幕
  33. Write(ts->connfd, buf, n); //回写给客户端
  34. }
  35. Close(ts->connfd);
  36. return (void *)0; //pthread_exit(0);
  37. }
  38. int main(void)
  39. {
  40. struct sockaddr_in servaddr, cliaddr;
  41. socklen_t cliaddr_len;
  42. int listenfd, connfd;
  43. pthread_t tid;
  44. struct s_info ts[256]; //创建结构体数组.
  45. int i = 0;
  46. listenfd = Socket(AF_INET, SOCK_STREAM, 0); //创建一个socket, 得到lfd
  47. bzero(&servaddr, sizeof(servaddr)); //地址结构清零
  48. servaddr.sin_family = AF_INET;
  49. servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //指定本地任意IP
  50. servaddr.sin_port = htons(SERV_PORT); //指定端口号
  51. Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); //绑定
  52. Listen(listenfd, 128); //设置同一时刻链接服务器上限数
  53. printf("Accepting client connect ...\n");
  54. while (1) {
  55. cliaddr_len = sizeof(cliaddr);
  56. connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); //阻塞监听客户端链接请求
  57. ts[i].cliaddr = cliaddr; //把文件描述符和客户端地地址结构组织到一个结构体变量中
  58. ts[i].connfd = connfd;
  59. pthread_create(&tid, NULL, do_work, (void*)&ts[i]);//表结构体变量作为参数传递到子线程
  60. pthread_detach(tid); //子线程分离,防止僵线程产生.
  61. i++;
  62. }
  63. return 0;
  64. }

执行输出如下: 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/496326
推荐阅读
相关标签
  

闽ICP备14008679号