当前位置:   article > 正文

分别通过select、多进程、多线程实现一个并发服务器

分别通过select、多进程、多线程实现一个并发服务器

select

  1. #include<myhead.h>
  2. #define PORT 8888 //端口号
  3. #define IP "192.168.114.109" //IP地址
  4. int main(int argc, const char *argv[])
  5. {
  6. //1、创建用于接受连接的套接字
  7. int sfd = socket(AF_INET, SOCK_STREAM, 0);
  8. if(sfd == -1)
  9. {
  10. perror("socket error");
  11. return -1;
  12. }
  13. printf("socket success\n");
  14. //设置端口号快速重用
  15. int reuse = 1;
  16. if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
  17. {
  18. perror("setsockopt error");
  19. return -1;
  20. }
  21. printf("设置端口快速重用成功\n");
  22. //绑定IP地址和端口号
  23. //填充要绑定的地址信息结构体
  24. struct sockaddr_in sin;
  25. sin.sin_family = AF_INET; //表明是ipv4
  26. sin.sin_port = htons(PORT); //端口号
  27. sin.sin_addr.s_addr = inet_addr(IP); //IP地址
  28. //绑定
  29. if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1)
  30. {
  31. perror("bind error");
  32. return -1;
  33. }
  34. printf("bind success\n");
  35. //套接字设置成被动监听状态
  36. if(listen(sfd, 128) == -1)
  37. {
  38. perror("listen error");
  39. return -1;
  40. }
  41. printf("listen success\n");
  42. //阻塞等待客户端连接请求,如果有新的客户端连接,则创建一个新的用于通信的套接字
  43. //定义客户端地址信息结构体
  44. struct sockaddr_in cin; //客户端地址信息结构体
  45. cin.sin_family = AF_INET;
  46. socklen_t socklen = sizeof(cin); //客户端地址信息的大小
  47. 定义一个用于检测文件描述符的集合
  48. fd_set readfds, tempfds; //在栈区定义
  49. 清空容器中的内容
  50. FD_ZERO(&readfds);
  51. 将要检测的文件描述符放入集合中
  52. FD_SET(sfd, &readfds); //将sfd文件描述符放入
  53. FD_SET(0, &readfds); //将0号文件描述符放入
  54. //定义一个容器
  55. char buf[128] = "";
  56. int res = 0; //接收select的返回值
  57. int newfd = -1; //存放用于最新连接客户端的套接字
  58. int maxfd = sfd; //定义控制select函数中最大文件描述符
  59. struct sockaddr_in saveCin[1024]; //用于存放客户端地址信息结构体
  60. while(1)
  61. {
  62. 将集合内容复制一份
  63. tempfds = readfds;
  64. 使用select阻塞等待集合中的文件描述符有事件产生
  65. res = select(maxfd+1, &tempfds, NULL, NULL, NULL);
  66. if(res == -1)
  67. {
  68. perror("select error");
  69. return -1;
  70. }else if(res == 0)
  71. {
  72. printf("time out\n");
  73. return -1;
  74. }
  75. //遍历所有集合中文件描述符
  76. for(int i=0; i<=maxfd; i++)
  77. {
  78. //判断当前i是否在集合中,如果不在,直接判断下一个
  79. if(!FD_ISSET(i, &tempfds))
  80. {
  81. continue;
  82. }
  83. 判断sfd是否还在集合中
  84. if( i == sfd)
  85. {
  86. //阻塞接收客户端的链接请求,并且获取客户端的地址信息
  87. newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);
  88. if(newfd == -1)
  89. {
  90. perror("accept error");
  91. return -1;
  92. }
  93. printf("accept success\n");
  94. 将newfd放入readfds中
  95. FD_SET(newfd , &readfds);
  96. //更新maxfd
  97. if(newfd > maxfd)
  98. {
  99. maxfd = newfd;
  100. }
  101. //将最新的客户端套接字放入数组的下标为new的位置
  102. saveCin[newfd] = cin;
  103. }else if(i == 0 ) //判断是否是终端输入
  104. {
  105. char buf1[128] = "";
  106. bzero(buf, sizeof(buf));
  107. //从终端获取数据
  108. fgets(buf, sizeof(buf), stdin); //从终端获取数据
  109. buf[strlen(buf)-1]='\0';
  110. printf("触发终端输入事件:%s\n", buf);
  111. sprintf(buf1, "%s%s", "系统消息:", buf);
  112. //将数据发送给所有客户端
  113. for(int j=4; j<=maxfd; j++)
  114. {
  115. send(j, buf1,sizeof(buf1), 0);
  116. }
  117. }else
  118. {
  119. //收发数据使用newfd完成通信
  120. char buf[128] = "";
  121. //清空字符串
  122. bzero(buf, sizeof(buf));
  123. int ret = recv(i, buf, sizeof(buf), 0); //从套接字中读取客户端发来的消息
  124. //判断收到的结果
  125. if(ret == 0)
  126. {
  127. printf("客户端已经下线\n");
  128. close(i); //关闭通信的套接字
  129. 将当前的文件描述符从集合中删除
  130. FD_CLR(i, &readfds);
  131. 更新maxfd
  132. for(int j=maxfd; j>=0; j--)
  133. {
  134. //判断当前的j是否在集合中,如果在,则为maxfd
  135. if(FD_ISSET(j, &readfds))
  136. {
  137. maxfd = j;
  138. continue; //继续判断下一个
  139. }
  140. }
  141. }else if(ret < 0)
  142. {
  143. perror("recv error");
  144. return -1;
  145. }
  146. printf("[%s:%d]:%s\n", inet_ntoa(saveCin[i].sin_addr), ntohs(saveCin[i].sin_port), buf);
  147. //将读取的信息,加上一些字符发送回去
  148. strcat(buf, "*_*");
  149. send(i, buf, sizeof(buf), 0);
  150. }
  151. }
  152. }
  153. //关闭所有套接字
  154. close(sfd);
  155. return 0;
  156. }

多进程

  1. #include<myhead.h>
  2. #define PORT 8888 //端口号
  3. #define IP "192.168.114.74" //IP地址
  4. //定义函数处理客户端信息
  5. int deal_cli_msg(int newfd, struct sockaddr_in cin)
  6. {
  7. //收发数据使用newfd完成通信
  8. char buf[128] = "";
  9. while(1)
  10. {
  11. //清空字符串
  12. bzero(buf, sizeof(buf));
  13. //read(newfd, buf, sizeof(buf)); //从套接字中读取客户端发来的消息
  14. int res = recv(newfd, buf, sizeof(buf), 0); //从套接字中读取客户端发来的消息
  15. //buf[strlen(buf)-1] = '\0';
  16. //判断收到的结果
  17. if(res == 0)
  18. {
  19. printf("客户端已经下线\n");
  20. break;
  21. }else if(res < 0)
  22. {
  23. perror("recv error");
  24. return -1;
  25. }
  26. printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);
  27. //将读取的信息,加上一些字符发送回去
  28. strcat(buf, "*_*");
  29. // write(newfd, buf, sizeof(buf));
  30. send(newfd, buf, sizeof(buf), 0);
  31. }
  32. close(newfd); //关闭通信的套接字
  33. return 0;
  34. }
  35. //定义信号处理函数
  36. void handler(int signo)
  37. {
  38. if(signo == SIGCHLD)
  39. {
  40. while(waitpid(-1, NULL, WNOHANG) > 0); //非阻塞形式回收僵尸进程
  41. }
  42. }
  43. int main(int argc, const char *argv[])
  44. {
  45. //创建用于接受连接的套接字
  46. int sfd = socket(AF_INET, SOCK_STREAM, 0);
  47. if(sfd == -1)
  48. {
  49. perror("socket error");
  50. return -1;
  51. }
  52. printf("socket success\n");
  53. //设置端口号快速重用
  54. int reuse = 1;
  55. if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
  56. {
  57. perror("setsockopt error");
  58. return -1;
  59. }
  60. printf("设置端口快速重用成功\n");
  61. //绑定IP地址和端口号
  62. //填充要绑定的地址信息结构体
  63. struct sockaddr_in sin;
  64. sin.sin_family = AF_INET; //表明是ipv4
  65. sin.sin_port = htons(PORT); //端口号
  66. sin.sin_addr.s_addr = inet_addr(IP); //IP地址
  67. //绑定
  68. if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1)
  69. {
  70. perror("bind error");
  71. return -1;
  72. }
  73. printf("bind success\n");
  74. //将套接字设置成被动监听状态
  75. if(listen(sfd, 128) == -1)
  76. {
  77. perror("listen error");
  78. return -1;
  79. }
  80. printf("listen success\n");
  81. //定义客户端地址信息结构体
  82. struct sockaddr_in cin; //客户端地址信息结构体
  83. cin.sin_family = AF_INET;
  84. socklen_t socklen = sizeof(cin); //客户端地址信息的大小
  85. //定义子进程变量
  86. pid_t pid;
  87. //将SIGCHLD信号绑定到自定义信号处理函数中
  88. if(signal(SIGCHLD, handler) == SIG_ERR)
  89. {
  90. perror("signal error");
  91. return -1;
  92. }
  93. while(1)
  94. {
  95. //阻塞接收客户端的链接请求,并且获取客户端的地址信息
  96. int newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);
  97. if(newfd == -1)
  98. {
  99. perror("accept error");
  100. return -1;
  101. }
  102. printf("accept success\n");
  103. //创建子进程,让子进程完成通信
  104. pid = fork();
  105. if(pid > 0)
  106. {
  107. //关闭newfd
  108. close(newfd);
  109. //回收僵尸进程
  110. //wait(NULL);
  111. }else if(pid == 0)
  112. {
  113. close(sfd);
  114. //调用处理客户端函数
  115. deal_cli_msg(newfd, cin);
  116. //退出子进程
  117. exit(EXIT_SUCCESS);
  118. }else
  119. {
  120. perror("fork error");
  121. return -1;
  122. }
  123. }
  124. //关闭所有套接字
  125. close(sfd);
  126. return 0;
  127. }

多线程

  1. #include<myhead.h>
  2. #define PORT 8888 //端口号
  3. #define IP "192.168.114.74" //IP地址
  4. //定义用于向线程体传参的结构体类型
  5. struct msg_info
  6. {
  7. int newfd;
  8. struct sockaddr_in cin;
  9. };
  10. //定义线程体函数
  11. void *deal_cli_msg(void *arg)
  12. {
  13. //获取主线程传递的信息
  14. int newfd = ((struct msg_info*)arg) -> newfd;
  15. struct sockaddr_in cin = ((struct msg_info*)arg) -> cin;
  16. //收发数据使用newfd完成通信
  17. char buf[128] = "";
  18. while(1)
  19. {
  20. //清空字符串
  21. bzero(buf, sizeof(buf));
  22. //read(newfd, buf, sizeof(buf)); //从套接字中读取客户端发来的消息
  23. int res = recv(newfd, buf, sizeof(buf), 0); //从套接字中读取客户端发来的消息
  24. //buf[strlen(buf)-1] = '\0';
  25. //判断收到的结果
  26. if(res == 0)
  27. {
  28. printf("客户端已经下线\n");
  29. break;
  30. }else if(res < 0)
  31. {
  32. perror("recv error");
  33. return NULL;
  34. }
  35. printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);
  36. //将读取的信息,加上一些字符发送回去
  37. strcat(buf, "*_*");
  38. // write(newfd, buf, sizeof(buf));
  39. send(newfd, buf, sizeof(buf), 0);
  40. }
  41. close(newfd); //关闭通信的套接字
  42. ptread_exit(NULL); //退出线程
  43. }
  44. int main(int argc, const char *argv[])
  45. {
  46. //创建用于接受连接的套接字
  47. int sfd = socket(AF_INET, SOCK_STREAM, 0);
  48. if(sfd == -1)
  49. {
  50. perror("socket error");
  51. return -1;
  52. }
  53. printf("socket success sfd = %d\n", sfd); //4
  54. //设置端口号快速重用
  55. int reuse = 1;
  56. if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
  57. {
  58. perror("setsockopt error");
  59. return -1;
  60. }
  61. printf("设置端口快速重用成功\n");
  62. //绑定IP地址和端口号
  63. //填充要绑定的地址信息结构体
  64. struct sockaddr_in sin;
  65. sin.sin_family = AF_INET; //表明是ipv4
  66. sin.sin_port = htons(PORT); //端口号
  67. sin.sin_addr.s_addr = inet_addr(IP); //IP地址
  68. //绑定
  69. if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1)
  70. {
  71. perror("bind error");
  72. return -1;
  73. }
  74. printf("bind success\n");
  75. //将套接字设置成被动监听状态
  76. if(listen(sfd, 128) == -1)
  77. {
  78. perror("listen error");
  79. return -1;
  80. }
  81. printf("listen success\n");
  82. //阻塞等待客户端连接请求,如果有新的客户端连接,则创建一个新的用于通信的套接字
  83. //客户端地址信息结构体
  84. struct sockaddr_in cin; //客户端地址信息结构体
  85. cin.sin_family = AF_INET;
  86. socklen_t socklen = sizeof(cin); //客户端地址信息的大小
  87. while(1)
  88. {
  89. //阻塞接收客户端的链接请求,并且获取客户端的地址信息
  90. int newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);
  91. if(newfd == -1)
  92. {
  93. perror("accept error");
  94. return -1;
  95. }
  96. printf("accept success\n");
  97. //定义用于向线程体传参的结构体变量
  98. struct msg_info info = {newfd, cin};
  99. //创建分支线程用于通信
  100. pthread_t tid;
  101. if(pthread_create(&tid, NULL, deal_cli_msg, &info) != 0)
  102. {
  103. printf("分支线程创建失败\n");
  104. return -1;
  105. }
  106. //将该线程分离
  107. if(pthread_detach(tid) != 0)
  108. {
  109. printf("分离失败\n");
  110. return -1;
  111. }
  112. }
  113. //关闭所有套接字
  114. close(sfd);
  115. return 0;
  116. }

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

闽ICP备14008679号