当前位置:   article > 正文

嵌入式学习记录5.20(TCP并发服务器)

嵌入式学习记录5.20(TCP并发服务器)

目录

一. TCP并发服务器

二 .多进程实现TCP并发服务器

2.1流程框架

2.2具体实现代码

三. 多线程实现并发服务器

3.1流程框架

3.2具体实现

一. TCP并发服务器

1> 由于循环服务器使用时,只能等到上一个客户端处理结束后,才能处理下一个客户端

2> 原因是:accept函数是阻塞函数,而数据收发也是阻塞任务,这两个任务目前是顺序执行,当一个任务阻塞时,另一个任务只能等待

3> 为了解决上述顺序执行的多个阻塞任务,让多个阻塞任务可以并发执行,我们可以引入多进程或多线程实现多任务并发执行

4> 多进行实现原理:父进程可以用于接受客户端的连接请求,并创建出一个子进程用于通信

子进程只负责完成跟客户端的通信

5> 多线程实现原理:主线程可以用于接受客户端的连接请求,并创建出一个分支线程用于通信

分支线程只负责完成跟客户端的通信

二 .多进程实现TCP并发服务器

2.1流程框架

  1. //定义信号处理函数
  2. void handler(int signo)
  3. {
  4. if(signo == SIGCHLD)
  5. {
  6. while(waitpid(-1, NULL, WNOHANG) != 0);
  7. }
  8. }
  9. //将SIGCHLD信号绑定到信号处理函数中
  10. signal(SIGCHLD, handler);
  11. sfd = socket(); //创建用于通信的套接字文件描述符
  12. bind(); //绑定ip地址和端口号
  13. listen(); //将套接字设置成被动监听状态
  14. while(1)
  15. {
  16. newfd = accept(); //阻塞等待客户端连接请求,并为其创建一个新的用于通信的套接字问津描述符
  17. pid = fork(); //创建子进程用于处理客户端
  18. if(pid > 0)
  19. {
  20. //父进程
  21. close(newfd);
  22. }elseif(pid == 0)
  23. {
  24. //跟当前客户端进行通信
  25. close(sfd); //关闭sfd
  26. recv(); //阻塞读取消息
  27. send(); //发送消息
  28. close(newfd); //关闭套接字
  29. exit(); //退出进程
  30. }
  31. }
  32. close(sfd); //关闭监听

2.2具体实现代码

  1. #include<myhead.h>
  2. #define SER_PORT 8888 //服务器端口号
  3. #define SER_IP "192.168.125.113" //服务器ip地址
  4. //定义信号处理函数
  5. void handler(int signo)
  6. {
  7. //判断要处理的信号
  8. if(signo == SIGCHLD)
  9. {
  10. while(waitpid(-1, NULL, WNOHANG) != 0);
  11. }
  12. }
  13. int main(int argc, const char *argv[])
  14. {
  15. //将子进程的SIGCHLD(17)信号
  16. //当子进程退出时,会向其父进程发送该信号
  17. if(signal(SIGCHLD, handler) == SIG_ERR)
  18. {
  19. perror("signal error");
  20. return -1;
  21. }
  22. //1、为通信创建一个端点
  23. int sfd = socket(AF_INET, SOCK_STREAM, 0);
  24. //参数1:说明使用的是ipv4通信域
  25. //参数2:说明使用的是TCP面向连接的通信方式
  26. //参数3:由于参数2中已经指定通信方式,填0即可
  27. if(sfd == -1)
  28. {
  29. perror("socket error");
  30. return -1;
  31. }
  32. printf("socket success sfd = %d\n", sfd); //3
  33. //调用端口号快速重用函数
  34. int reuse = 1;
  35. if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
  36. {
  37. perror("setsockopt error");
  38. return -1;
  39. }
  40. //2、绑定ip和端口号
  41. //2.1 准备地址信息结构体
  42. struct sockaddr_in sin;
  43. sin.sin_family = AF_INET; //通信域
  44. sin.sin_port = htons(SER_PORT); //端口号
  45. sin.sin_addr.s_addr = inet_addr(SER_IP); //ip地址
  46. //2.2 绑定工作
  47. if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) ==-1)
  48. {
  49. perror("bind error");
  50. return -1;
  51. }
  52. printf("bind success\n");
  53. //3、将套接字设置成被动监听状态
  54. if(listen(sfd, 128)==-1)
  55. {
  56. perror("listen error");
  57. return -1;
  58. }
  59. printf("listen success\n");
  60. //4、阻塞等待客户端的连接
  61. //4.1 定义用于接受客户端信息的容器
  62. struct sockaddr_in cin;
  63. socklen_t addrlen = sizeof(cin);
  64. int newfd = -1; //客户端套接字变量
  65. while(1)
  66. {
  67. //父进程
  68. newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
  69. if(newfd == -1)
  70. {
  71. perror("accept error");
  72. return -1;
  73. }
  74. printf("[%s:%d]:发来连接请求\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
  75. pid_t pid = fork(); //创建子进程
  76. if(pid > 0)
  77. {
  78. //父进程体
  79. //关闭newfd
  80. close(newfd);
  81. }else if(pid == 0)
  82. {
  83. //关闭sfd
  84. close(sfd);
  85. //5、与客户端进行相互通信
  86. char rbuf[128] = ""; //读取消息内容的容器
  87. while(1)
  88. {
  89. //清空容器
  90. bzero(rbuf, sizeof(rbuf));
  91. //从套接字中读取数据
  92. //int res = read(newfd, rbuf, sizeof(rbuf));
  93. int res = recv(newfd, rbuf, sizeof(rbuf), 0);
  94. if(res == 0)
  95. {
  96. printf("客户端已经下线\n");
  97. break;
  98. }
  99. //将读取的消息展示出来
  100. printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), rbuf);
  101. //将收到的消息处理一下,回复给客户端
  102. strcat(rbuf, "*_*");
  103. //讲消息发送给客户端
  104. //write(newfd, rbuf, strlen(rbuf));
  105. send(newfd, rbuf, strlen(rbuf), 0);
  106. printf("发送成功\n");
  107. }
  108. //6、关闭套接字
  109. close(newfd);
  110. //退出子进程
  111. exit(EXIT_SUCCESS);
  112. }else
  113. {
  114. perror("fork error");
  115. return -1;
  116. }
  117. }
  118. close(sfd);
  119. return 0;
  120. }

三.多线程实现并发服务器

3.1流程框架

  1. //定义线程体函数
  2. void *deal_cli_msg(void *arg)
  3. {
  4. //跟当前客户端进行通信
  5. recv(); //阻塞读取消息
  6. send(); //发送消息
  7. close(newfd); //关闭套接字
  8. pthread_exit(NULL); //退出线程
  9. }
  10. sfd = socket(); //创建用于通信的套接字文件描述符
  11. bind(); //绑定ip地址和端口号
  12. listen(); //将套接字设置成被动监听状态
  13. while(1)
  14. {
  15. newfd = accept(); //阻塞等待客户端连接请求,并为其创建一个新的用于通信的套接字问津描述符
  16. pthread_create(&tid, NULL, deal_cli_msg, &info); //创建分支线程用于跟客户端进行通信
  17. pthread_detach(tid); //将线程设置成分离态
  18. }
  19. close(sfd); //关闭监听

3.2具体实现代码

  1. #include<myhead.h>
  2. #define SER_PORT 8888 //服务器端口号
  3. #define SER_IP "192.168.125.113" //服务器ip地址
  4. //定义一个结构体类型,用于向线程体函数传递参数
  5. struct 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 Info*)arg)->newfd;
  15. struct sockaddr_in cin = ((struct Info*)arg)->cin;
  16. //5、与客户端进行相互通信
  17. char rbuf[128] = ""; //读取消息内容的容器
  18. while(1)
  19. {
  20. //清空容器
  21. bzero(rbuf, sizeof(rbuf));
  22. //从套接字中读取数据
  23. //int res = read(newfd, rbuf, sizeof(rbuf));
  24. int res = recv(newfd, rbuf, sizeof(rbuf), 0);
  25. if(res == 0)
  26. {
  27. printf("客户端已经下线\n");
  28. break;
  29. }
  30. //将读取的消息展示出来
  31. printf("[%s:%d]:%s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), rbuf);
  32. //将收到的消息处理一下,回复给客户端
  33. strcat(rbuf, "*_*");
  34. //讲消息发送给客户端
  35. //write(newfd, rbuf, strlen(rbuf));
  36. send(newfd, rbuf, strlen(rbuf), 0);
  37. printf("发送成功\n");
  38. }
  39. //6、关闭套接字
  40. close(newfd);
  41. //退出线程
  42. pthread_exit(NULL);
  43. }
  44. int main(int argc, const char *argv[])
  45. {
  46. //1、为通信创建一个端点
  47. int sfd = socket(AF_INET, SOCK_STREAM, 0);
  48. //参数1:说明使用的是ipv4通信域
  49. //参数2:说明使用的是TCP面向连接的通信方式
  50. //参数3:由于参数2中已经指定通信方式,填0即可
  51. if(sfd == -1)
  52. {
  53. perror("socket error");
  54. return -1;
  55. }
  56. printf("socket success sfd = %d\n", sfd); //3
  57. //2、绑定ip和端口号
  58. //2.1 准备地址信息结构体
  59. struct sockaddr_in sin;
  60. sin.sin_family = AF_INET; //通信域
  61. sin.sin_port = htons(SER_PORT); //端口号
  62. sin.sin_addr.s_addr = inet_addr(SER_IP); //ip地址
  63. //2.2 绑定工作
  64. if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) ==-1)
  65. {
  66. perror("bind error");
  67. return -1;
  68. }
  69. printf("bind success\n");
  70. //3、将套接字设置成被动监听状态
  71. if(listen(sfd, 128)==-1)
  72. {
  73. perror("listen error");
  74. return -1;
  75. }
  76. printf("listen success\n");
  77. //4、阻塞等待客户端的连接
  78. //4.1 定义用于接受客户端信息的容器
  79. struct sockaddr_in cin;
  80. socklen_t addrlen = sizeof(cin);
  81. while(1)
  82. {
  83. //accept函数会预选一个当前未分配的最小的文件描述符
  84. //即使在阻塞过程中,有更小的文件描述符产生,本次操作的文件描述符也不会更改了
  85. int newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
  86. if(newfd == -1)
  87. {
  88. perror("accept error");
  89. return -1;
  90. }
  91. printf("[%s:%d]:发来连接请求, newfd = %d\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);
  92. //定义要传递数据的结构体变量
  93. struct Info buf = {newfd, cin};
  94. //创建分支线程,用于通信
  95. pthread_t tid = -1;
  96. if(pthread_create(&tid, NULL, deal_cli_msg, &buf) == -1)
  97. {
  98. printf("pthread_create error\n");
  99. return -1;
  100. }
  101. //将线程设置成分离态
  102. pthread_detach(tid);
  103. }
  104. close(sfd);
  105. return 0;
  106. }

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

闽ICP备14008679号