• 嵌入式学习记录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
    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
    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. }

  • 相关阅读:
    HTML中script 标签中的那些属性
    【第六部分 | JavaScript高级】1:面向对象
    python爬虫100例教程 python爬虫实例100例子
    安装konga
    Hive Java API操作
    Ansible-常用模块
    gd32关于IO引脚配置的一些问题
    [附源码]Python计算机毕业设计Django基于vuejs的爱宠用品销售app
    教师教学质量评价管理系统(ASP.net+SqlServer)
    java.lang.NoClassDefFoundError: org/apache/commons/collections/FastHashMap报错解决!
  • 原文地址:https://blog.csdn.net/weixin_53529699/article/details/139096471