• TCP 通信并发服务器详解(附有案例代码)


    一、多进程实现 TCP 通信并发服务器

    1、多进程实现通信并发服务器思路

    (1)一个父进程,多个子进程;

    (2)父进程负责等待并接受客户端的连接;

    (3)子进程:完成通信,接受一个客户端连接,就创建一个子进程用于通信。

    2、多进程实现通信并发服务器案例代码

    (1)客户端代码 client.c

    1. // TCP通信的客户端
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. int main() {
    8. // 1.创建套接字
    9. int fd = socket(AF_INET, SOCK_STREAM, 0);
    10. if(fd == -1) {
    11. perror("socket");
    12. exit(-1);
    13. }
    14. // 2.连接服务器端
    15. struct sockaddr_in serveraddr;
    16. serveraddr.sin_family = AF_INET;
    17. inet_pton(AF_INET, "192.168.172.128", &serveraddr.sin_addr.s_addr); //对应自己电脑的IP地址
    18. serveraddr.sin_port = htons(9999);
    19. int ret = connect(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    20. if(ret == -1) {
    21. perror("connect");
    22. exit(-1);
    23. }
    24. // 3. 通信
    25. char recvBuf[1024];
    26. int i = 0;
    27. while(1) {
    28. sprintf(recvBuf, "data : %d\n", i++);
    29. // 给服务器端发送数据
    30. write(fd, recvBuf, strlen(recvBuf)+1); //+1是加上字符长结束字符
    31. int len = read(fd, recvBuf, sizeof(recvBuf));
    32. if(len == -1) {
    33. perror("read");
    34. exit(-1);
    35. } else if(len > 0) {
    36. printf("recv server : %s\n", recvBuf);
    37. } else if(len == 0) {
    38. // 表示服务器端断开连接
    39. printf("server closed...");
    40. break;
    41. }
    42. sleep(1);
    43. }
    44. // 关闭连接
    45. close(fd);
    46. return 0;
    47. }

    (2)服务端代码 server_process.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. void recyleChild(int arg) {
    10. while(1) {
    11. int ret = waitpid(-1, NULL, WNOHANG);
    12. if(ret == -1) {
    13. // 所有的子进程都回收了
    14. break;
    15. }else if(ret == 0) {
    16. // 还有子进程活着
    17. break;
    18. } else if(ret > 0){
    19. // 被回收了
    20. printf("子进程 %d 被回收了\n", ret);
    21. }
    22. }
    23. }
    24. int main() {
    25. struct sigaction act;
    26. act.sa_flags = 0;
    27. sigemptyset(&act.sa_mask);
    28. act.sa_handler = recyleChild;
    29. // 注册信号捕捉,释放资源,此时不能用wait,该函数会阻塞影响通信
    30. sigaction(SIGCHLD, &act, NULL);
    31. // 创建socket
    32. int lfd = socket(PF_INET, SOCK_STREAM, 0);
    33. if(lfd == -1){
    34. perror("socket");
    35. exit(-1);
    36. }
    37. struct sockaddr_in saddr;
    38. saddr.sin_family = AF_INET;
    39. saddr.sin_port = htons(9999);
    40. saddr.sin_addr.s_addr = INADDR_ANY;
    41. // 绑定
    42. int ret = bind(lfd,(struct sockaddr *)&saddr, sizeof(saddr));
    43. if(ret == -1) {
    44. perror("bind");
    45. exit(-1);
    46. }
    47. // 监听
    48. ret = listen(lfd, 128);
    49. if(ret == -1) {
    50. perror("listen");
    51. exit(-1);
    52. }
    53. // 不断循环等待客户端连接
    54. while(1) {
    55. struct sockaddr_in cliaddr;
    56. int len = sizeof(cliaddr);
    57. // 接受连接
    58. int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len);
    59. if(cfd == -1) {
    60. if(errno == EINTR) {
    61. continue;
    62. }
    63. perror("accept");
    64. exit(-1);
    65. }
    66. // 每一个连接进来,创建一个子进程跟客户端通信
    67. pid_t pid = fork();
    68. if(pid == 0) {
    69. // 子进程
    70. // 获取客户端的信息
    71. char cliIp[16];
    72. inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, cliIp, sizeof(cliIp));
    73. unsigned short cliPort = ntohs(cliaddr.sin_port);
    74. printf("client ip is : %s, prot is %d\n", cliIp, cliPort);
    75. // 接收客户端发来的数据
    76. char recvBuf[1024];
    77. while(1) {
    78. int len = read(cfd, &recvBuf, sizeof(recvBuf));
    79. if(len == -1) {
    80. perror("read");
    81. exit(-1);
    82. }else if(len > 0) {
    83. printf("recv client : %s\n", recvBuf);
    84. } else if(len == 0) {
    85. printf("client closed....\n");
    86. break;
    87. }
    88. write(cfd, recvBuf, strlen(recvBuf) + 1);
    89. }
    90. close(cfd);
    91. exit(0); // 退出当前子进程
    92. }
    93. }
    94. close(lfd);
    95. return 0;
    96. }

    二、多线程实现 TCP 通信并发服务器 

    1、多进程实现通信并发服务器思路

    (1)一个父线程,多个子线程;

    (2)父线程负责等待并接受客户端的连接;

    (3)子线程:完成通信,接受一个客户端连接,就创建一个子线程用于通信。

    2、多线程实现通信并发服务器案例代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. struct sockInfo {
    8. int fd; // 通信的文件描述符
    9. struct sockaddr_in addr;
    10. pthread_t tid; // 线程号
    11. };
    12. struct sockInfo sockinfos[128];
    13. void * working(void * arg) {
    14. // 子线程和客户端通信 cfd 客户端的信息 线程号
    15. // 获取客户端的信息
    16. struct sockInfo * pinfo = (struct sockInfo *)arg;
    17. char cliIp[16];
    18. inet_ntop(AF_INET, &pinfo->addr.sin_addr.s_addr, cliIp, sizeof(cliIp));
    19. unsigned short cliPort = ntohs(pinfo->addr.sin_port);
    20. printf("client ip is : %s, prot is %d\n", cliIp, cliPort);
    21. // 接收客户端发来的数据
    22. char recvBuf[1024];
    23. while(1) {
    24. int len = read(pinfo->fd, &recvBuf, sizeof(recvBuf));
    25. if(len == -1) {
    26. perror("read");
    27. exit(-1);
    28. }else if(len > 0) {
    29. printf("recv client : %s\n", recvBuf);
    30. } else if(len == 0) {
    31. printf("client closed....\n");
    32. break;
    33. }
    34. write(pinfo->fd, recvBuf, strlen(recvBuf) + 1);
    35. }
    36. close(pinfo->fd);
    37. return NULL;
    38. }
    39. int main() {
    40. // 创建socket
    41. int lfd = socket(PF_INET, SOCK_STREAM, 0);
    42. if(lfd == -1){
    43. perror("socket");
    44. exit(-1);
    45. }
    46. struct sockaddr_in saddr;
    47. saddr.sin_family = AF_INET;
    48. saddr.sin_port = htons(9999);
    49. saddr.sin_addr.s_addr = INADDR_ANY;
    50. // 绑定
    51. int ret = bind(lfd,(struct sockaddr *)&saddr, sizeof(saddr));
    52. if(ret == -1) {
    53. perror("bind");
    54. exit(-1);
    55. }
    56. // 监听
    57. ret = listen(lfd, 128);
    58. if(ret == -1) {
    59. perror("listen");
    60. exit(-1);
    61. }
    62. // 初始化数据
    63. int max = sizeof(sockinfos) / sizeof(sockinfos[0]);
    64. for(int i = 0; i < max; i++) {
    65. bzero(&sockinfos[i], sizeof(sockinfos[i]));
    66. sockinfos[i].fd = -1;
    67. sockinfos[i].tid = -1;
    68. }
    69. // 循环等待客户端连接,一旦一个客户端连接进来,就创建一个子线程进行通信
    70. while(1) {
    71. struct sockaddr_in cliaddr;
    72. int len = sizeof(cliaddr);
    73. // 接受连接
    74. int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &len);
    75. struct sockInfo * pinfo;
    76. for(int i = 0; i < max; i++) {
    77. // 从这个数组中找到一个可以用的sockInfo元素
    78. if(sockinfos[i].fd == -1) {
    79. pinfo = &sockinfos[i];
    80. break;
    81. }
    82. if(i == max - 1) {
    83. sleep(1);
    84. i--;
    85. }
    86. }
    87. pinfo->fd = cfd;
    88. memcpy(&pinfo->addr, &cliaddr, len);
    89. // 创建子线程
    90. pthread_create(&pinfo->tid, NULL, working, pinfo);
    91. pthread_detach(pinfo->tid);
    92. }
    93. close(lfd);
    94. return 0;
    95. }

  • 相关阅读:
    【补题日记】[2022牛客暑期多校4]A-Task Computing
    如何监听电脑屏幕是否发生切换
    PAT 甲级常见单词整理
    【seata】引入seata导致原本自定义实现的RequestInterceptor失效
    无痛入门Prometheus:一个强大的开源监控和告警系统,如何快速安装和使用?
    传输层之UDP协议
    【Java面试】第三章:P6级面试
    PTA:7-3 两个递增链表的差集
    html网页如何获取后台数据库的数据(html + ajax + php + mysql)
    day 1
  • 原文地址:https://blog.csdn.net/weixin_47156401/article/details/125868728