• 【高性能服务器】多线程并发模型


    🔥博客主页: 我要成为C++领域大神
    🎥系列专栏【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
    ❤️感谢大家点赞👍收藏⭐评论✍️

    本博客致力于知识分享,与更多的人进行学习交流

    ​​

    对于常见的C/S模型,一个服务端通常需要服务多个客户端。如果使用单行的处理模型,当新的客户端请求服务端的服务时,就必须等待比它先到的客户端的请求全部完成。

    因此引入多进程并发服务器模型。多进程并发服务器模型的简单流程图如下所示。父进程创建一个套接字,然后与自己的IP地址、端口号进行绑定。之后调用开始监听来自客户端的敲门,当有客户端来敲门时,accept()接收客户端的连接并创建一个新套接字用于与客户端通信。接下来调用fork()函数,当调用fork()函数时,操作系统会复制当前进程的一个副本,包括进程的代码、数据和状态等信息。如果其返回值为负数,表示创建子进程失败。否则他在父子进程中有不同的返回值:如果返回值为0,表示当前代码正在子进程中执行。如果返回值大于0,表示当前代码正在父进程中执行,返回的值是子进程的进程ID。因此可以使用if-else语句来编写子进程的处理代码。

    在子进程中,先关闭从父进程中复制下来监听套接字,这个套接字在子进程中没有用了,纯属浪费资源,之后再进行与客户端的通信。而在父进程中,同理关闭accept()创建的新套接字,然后继续监听客户端的连接请求。

    而同一进程的所有线程共享相同的内存空间,线程数据共享和通信更加方便,创建和管理线程的资源消耗较少,尤其是内存开销较小。由于线程间通信和进程间通信,多线程模型在处理大量任务时响应速度较快。

    在多进程+多线程代码的基础上,将进行accept的进程修改为创建的线程工作,socket通信的功能放在线程工作函数内。

    使用服务器测试业务:

    客户端向标准输入发送小写字符串,服务端响应回复对应大写字符,"abcAS"->"ABCAS"

    客户端向服务端发送关键字localtime,服务端响应回复系统时间、

    服务端: 

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #define _SERVER_IP "xxx.xxx.xxx.xxx"
    13. #define _PORT 8080
    14. #define _BACKLOG 128
    15. #define _SHUTDOWN 1
    16. #define _TRUE 1
    17. #define _FALSE 0
    18. #define _IPSIZE 16
    19. #define _RECVLEN 1500
    20. struct client_info
    21. {
    22. int client_fd;
    23. struct sockaddr_in clientAddr;
    24. };
    25. void sig_wait(int n)
    26. {
    27. pid_t zpid;
    28. while ((zpid = waitpid(-1, NULL, WNOHANG)) > 0)
    29. {
    30. printf("wait Thread Tid [0x%x] Wait Successfully,Zombie %d\n", (unsigned int)pthread_self(), zpid);
    31. }
    32. }
    33. void *thread_wait(void *arg)
    34. {
    35. // 设定信号捕捉
    36. pthread_detach(pthread_self());
    37. struct sigaction act, oact;
    38. act.sa_handler = sig_wait;
    39. act.sa_flags = 0;
    40. sigemptyset(&act.sa_mask);
    41. sigaction(SIGCHLD, &act, &oact);
    42. // 解除屏蔽
    43. sigprocmask(SIG_SETMASK, &act.sa_mask, NULL);
    44. printf("wait Thread [0x%x] is Waiting...\n", (unsigned int)pthread_self());
    45. while (1)
    46. sleep(1);
    47. pthread_exit(NULL);
    48. }
    49. void *thread_recv(void *arg)
    50. {
    51. int recvlen;
    52. char Result[_RECVLEN];
    53. char client_ip[_IPSIZE];
    54. time_t tp;
    55. char time_buf[100]; // 存放当前系统时间
    56. int toupper_flag;
    57. struct client_info cinfo = *((struct client_info *)arg);
    58. // Socket通信
    59. bzero(Result, sizeof(Result));
    60. bzero(client_ip, sizeof(client_ip));
    61. inet_ntop(AF_INET, &cinfo.clientAddr.sin_addr.s_addr, client_ip, _IPSIZE);
    62. printf("Connection From :IP[%s],PORT[%d]\n", client_ip, ntohs(cinfo.clientAddr.sin_port));
    63. sprintf(Result, "Hi [%s] Welcome to my TCP test server!service version 1.1.0...", client_ip);
    64. send(cinfo.client_fd, Result, strlen(Result), 0);
    65. bzero(Result, sizeof(Result));
    66. // 读取用户数据,如果用户发的是普通小写字符字符串,转换为大写,如果发送的是local关键字,响应时间
    67. // 持续响应,循环读写
    68. while ((recvlen = recv(cinfo.client_fd, Result, sizeof(Result), 0)) > 0)
    69. { // 处理客户端业务
    70. printf("Client Say:%s\n", Result);
    71. if (strcmp(Result, "localtime") == 0)
    72. {
    73. tp = time(NULL); // 获取时间种子
    74. ctime_r(&tp, time_buf);
    75. time_buf[strcspn(time_buf, "\n")] = '\0';
    76. printf("[%s]Response SysTime Successfully!\n", client_ip);
    77. send(cinfo.client_fd, time_buf, strlen(time_buf) + 1, 0);
    78. }
    79. else
    80. {
    81. toupper_flag = 0;
    82. while (recvlen > toupper_flag)
    83. {
    84. Result[toupper_flag] = toupper(Result[toupper_flag]);
    85. ++toupper_flag;
    86. }
    87. printf("[%s]Response Toupper Successfully!\n", client_ip);
    88. send(cinfo.client_fd, Result, recvlen, 0);
    89. }
    90. }
    91. if (recvlen == 0) // 客户端退出
    92. {
    93. close(cinfo.client_fd);
    94. printf("[%s] is Exiting,Kill Child\n", client_ip);
    95. exit(0);
    96. }
    97. close(cinfo.client_fd);
    98. }
    99. int main()
    100. {
    101. struct sockaddr_in serverAddr, clientAddr;
    102. int server_fd;
    103. int client_fd;
    104. // 主线程设置屏蔽
    105. sigset_t set, oldset;
    106. sigemptyset(&set);
    107. sigaddset(&set, SIGCHLD);
    108. sigprocmask(SIG_SETMASK, &set, &oldset);
    109. pthread_t tid;
    110. pthread_create(&tid, NULL, thread_wait, NULL); // 创建回收线程
    111. socklen_t Addrlen;
    112. serverAddr.sin_family = AF_INET;
    113. serverAddr.sin_port = htons(_PORT);
    114. serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    115. server_fd = socket(AF_INET, SOCK_STREAM, 0);
    116. bind(server_fd, (struct sockaddr *)&serverAddr, sizeof(serverAddr));
    117. listen(server_fd, _BACKLOG);
    118. printf("Test TCP Server Version 1.1.0 is Running...\n");
    119. while (_SHUTDOWN)
    120. {
    121. Addrlen = sizeof(clientAddr);
    122. if ((client_fd = accept(server_fd, (struct sockaddr *)&clientAddr, &Addrlen)) > 0)
    123. {
    124. struct client_info cinfo;
    125. cinfo.client_fd = client_fd;
    126. cinfo.clientAddr = clientAddr;
    127. pthread_create(&tid, NULL, thread_recv, &cinfo); // 创建工作线程
    128. }
    129. else
    130. {
    131. // accept失败,测试中断
    132. close(server_fd);
    133. if (errno == EINTR)
    134. {
    135. printf("Accept ERROR Eintr...\n");
    136. exit(0);
    137. }
    138. }
    139. }
    140. close(server_fd);
    141. return 0;
    142. }

    客户端:

    1. #ifndef _MYSOCK_H_
    2. #define _MYSOCK_H_
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include
    15. int SOCKET(int domain, int type, int protocol);
    16. int BIND(int sockfd, struct sockaddr* addr, socklen_t addrlen);
    17. ssize_t RECV(int sockfd, void* buf, size_t len, int flags);
    18. ssize_t SEND(int sockfd, void* buf, size_t len, int flags);
    19. int CONNECT(int sockfd, struct sockaddr* addr, socklen_t addrlen);
    20. int ACCEPT(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
    21. int LISTEN(int sockfd, int backlog);
    22. char* FGETS(char* s, int size, FILE* stream);
    23. int SELECT(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,
    24. struct timeval* timeout);
    25. int socket_init();
    26. int return_response(int clientfd, const char* clientip);
    27. //void strDeal(int *client_fd);
    28. // 全局变量声明
    29. char recv_buf[1024];
    30. char time_buf[64];
    31. int serverFd, clientFd;
    32. struct sockaddr_in clientAddr;
    33. fd_set set, oset;
    34. int client_array[1020];
    35. int maxfd, ready;
    36. socklen_t addrlen;
    37. char clientip[16];
    38. time_t tp;
    39. ssize_t recvlen;
    40. int toupper_flag;
    41. #define SHUTDOWN 1
    42. #endif
    1. #include "MySock.h"
    2. //客户端源码编写,连接服务器成功,服务器反馈信息
    3. #define _IP "xxx.xxx.xxx.xxx"
    4. #define _PORT 8080
    5. int main()
    6. {
    7. struct sockaddr_in ServerAddr;
    8. bzero(&ServerAddr,sizeof(ServerAddr));
    9. ServerAddr.sin_family=AF_INET;
    10. ServerAddr.sin_port=htons(_PORT);
    11. inet_pton(AF_INET,_IP,&ServerAddr.sin_addr.s_addr);
    12. int Myfd=SOCKET(AF_INET,SOCK_STREAM,0);
    13. //看需求决定是否要绑定
    14. char Response[1024];//存放服务端反馈信息
    15. ssize_t recvlen;
    16. bzero(Response,sizeof(Response));
    17. char sendbuf[1024];
    18. if((CONNECT(Myfd,(struct sockaddr *)&ServerAddr,sizeof(ServerAddr)))==0)
    19. {
    20. while(1)
    21. {
    22. if((recvlen=RECV(Myfd,Response,sizeof(Response),0))>0)
    23. {
    24. printf("%s\n",Response);
    25. }
    26. printf("Please Type Some text:");//读取标准输入发送给服务端
    27. FGETS(sendbuf,sizeof(sendbuf),stdin);
    28. sendbuf[strcspn(sendbuf,"\n")]='\0';
    29. SEND(Myfd,sendbuf,sizeof(sendbuf),0);
    30. }
    31. }
    32. close(Myfd);
    33. printf("Client is Over\n");
    34. return 0;
    35. }

  • 相关阅读:
    MSDC 4.3 接口规范(23)
    uniapp中videojs、renderjs的使用
    vue3速查笔记
    如何深入理解JSX和React组件?
    编程题练习@9-7
    css实现鼠标悬停时元素的显示与隐藏
    javaweb 通过JDBC连接Mysql 数据库
    Rome链分析
    docker离线安装并导入镜像
    基于SpringBoot+Vue的MOBA类游戏攻略分享平台
  • 原文地址:https://blog.csdn.net/Coldreams/article/details/140119520