• 国庆作业 10月1 用select实现服务器并发


    1. #include
    2. #define ERR_MSG(msg) \
    3. do \
    4. { \
    5. fprintf(stderr, "__%d__:", __LINE__); \
    6. perror(msg); \
    7. } while (0)
    8. #define IP "192.168.221.131"
    9. #define PORT 8080
    10. int keybord_event(fd_set readfds);
    11. int cliConnect_event(int, struct sockaddr_in *, fd_set *, int *);
    12. int cliRcvSnd_event(int, struct sockaddr_in *, fd_set *, int *);
    13. int main(int argc, const char *argv[])
    14. {
    15. //创建流失套接字
    16. int sfd = socket(AF_INET, SOCK_STREAM, 0);
    17. if (sfd < 0)
    18. {
    19. ERR_MSG("socket");
    20. return -1;
    21. }
    22. printf("socket create success sfd = % d\n", sfd);
    23. //设置端口可以快速被复用
    24. int reuse = 1;
    25. if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    26. {
    27. ERR_MSG("setsockopt");
    28. return -1;
    29. }
    30. printf("允许端口快速被复用成功\n");
    31. //填充地址信息给bind函数
    32. struct sockaddr_in sin;
    33. sin.sin_family = AF_INET;
    34. sin.sin_port = htons(PORT);
    35. sin.sin_addr.s_addr = inet_addr(IP);
    36. // bind 绑定服务器
    37. if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    38. {
    39. ERR_MSG("bind");
    40. return -1;
    41. }
    42. printf("bind success\n");
    43. //将套接字设置为监听状态
    44. if (listen(sfd, 100) < 0)
    45. {
    46. ERR_MSG("linsten");
    47. return -1;
    48. }
    49. printf("listen success\n");
    50. //创建一个读合集
    51. fd_set readfds, tempfds;
    52. FD_ZERO(&readfds); //清空集合
    53. //将要检测的文件描述符添加到集合中
    54. FD_SET(0, &readfds);
    55. FD_SET(sfd, &readfds);
    56. int maxfd = sfd; //存储最大的文件描述符
    57. int s_res = -1;
    58. ssize_t res = -1;
    59. char buf[128] = "";
    60. struct sockaddr_in saveCin[1024]; //备份连接成功的客户端信息,用下标来对应文件描述符
    61. while (1)
    62. {
    63. tempfds = readfds;
    64. // select函数
    65. s_res = select(maxfd + 1, &tempfds, NULL, NULL, NULL);
    66. if (s_res < 0)
    67. {
    68. ERR_MSG("select");
    69. return -1;
    70. }
    71. else if (0 == s_res)
    72. {
    73. printf("time out\n");
    74. break;
    75. }
    76. for (int i = 0; i <= maxfd; i++)
    77. {
    78. if (FD_ISSET(i, &tempfds)==0)
    79. continue;
    80. if (0 == i) // 0为stdin,也就是有终端输入了
    81. {
    82. printf("键盘输入事件\n");
    83. keybord_event(readfds);
    84. }
    85. else if (sfd == i)
    86. {
    87. printf("客户端连接事件\n");
    88. cliConnect_event(sfd, saveCin, &readfds, &maxfd);
    89. }
    90. else
    91. {
    92. printf("客户端交互事件\n");
    93. cliRcvSnd_event(i, saveCin, &readfds, &maxfd);
    94. }
    95. }
    96. }
    97. if (close(sfd) < 0)
    98. {
    99. ERR_MSG("close");
    100. return -1;
    101. }
    102. return 0;
    103. }
    104. //键盘输入事件的函数体
    105. int keybord_event(fd_set readfds)
    106. {
    107. char buf[128] = "";
    108. int sndfd = -1;
    109. memset(buf, 0, sizeof(buf));
    110. int res = scanf("%d %s", &sndfd, buf);
    111. while (getchar() != 10)
    112. ;
    113. if (res != 2)
    114. {
    115. printf("输入的数据格式有误 :fd string\n");
    116. return -1;
    117. }
    118. if (sndfd <= 2 || FD_ISSET(sndfd, &readfds) == 0)
    119. {
    120. printf("非法的文件描述符\n");
    121. return -1;
    122. }
    123. // 将文件描述符设置为非阻塞模式
    124. int flags = fcntl(sndfd, F_GETFL, 0);
    125. flags |= O_NONBLOCK;
    126. if (fcntl(sndfd, F_SETFL, flags) == -1)
    127. {
    128. ERR_MSG("fcntl");
    129. return -1;
    130. }
    131. if (send(sndfd, buf, sizeof(buf), 0) < 0)
    132. {
    133. ERR_MSG("send");
    134. return -1;
    135. }
    136. printf("send success\n");
    137. return 0;
    138. }
    139. //客户端连接事件的函数体
    140. int cliConnect_event(int sfd, struct sockaddr_in saveCin[], fd_set *preadfds, int *pmaxfd)
    141. {
    142. int newfd = -1;
    143. struct sockaddr_in cin; //存储客户端的地址信息
    144. memset(&cin, 0, sizeof(cin));
    145. socklen_t addrlen = sizeof(cin); //真实的地址信息结构体的大小
    146. newfd = accept(sfd, (struct sockaddr *)&cin, &addrlen);
    147. if (newfd < 0)
    148. {
    149. ERR_MSG("accept");
    150. return -1;
    151. }
    152. printf("[%s:%d]客户端连接成功 newfd = %d\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);
    153. saveCin[newfd] = cin; //将cin另存到newfd对应的下标
    154. FD_SET(newfd, preadfds); //将newfd添加到集合
    155. *pmaxfd = *pmaxfd > newfd ? *pmaxfd : newfd; //更新maxfd
    156. return 0;
    157. }
    158. //客户端交互事件的函数体
    159. int cliRcvSnd_event(int fd, struct sockaddr_in *saveCin, fd_set *preadfds, int *pmaxfd)
    160. {
    161. char buf[128] = "";
    162. memset(buf, 0, sizeof(buf));
    163. //接收数据
    164. ssize_t res = recv(fd, buf, sizeof(buf), 0);
    165. if (res < 0)
    166. {
    167. ERR_MSG("recv");
    168. return -1;
    169. }
    170. else if (0 == res)
    171. {
    172. printf("[%s:%d]客户端连接关闭 newfd = %d\n", inet_ntoa(saveCin[fd].sin_addr), ntohs(saveCin[fd].sin_port), fd);
    173. close(fd);
    174. FD_CLR(fd, preadfds);
    175. while (FD_ISSET(*pmaxfd, preadfds) == 0 && (*pmaxfd)-- >= 0)
    176. ;
    177. return 0;
    178. }
    179. printf("[%s:%d] newfd=%d : %s\n", inet_ntoa(saveCin[fd].sin_addr), ntohs(saveCin[fd].sin_port), fd, buf);
    180. //发送数据
    181. strcat(buf, "已收到");
    182. if (send(fd, buf, sizeof(buf), 0) < 0)
    183. {
    184. ERR_MSG("send");
    185. return -1;
    186. }
    187. printf("send success\n");
    188. return 0;
    189. }

     

  • 相关阅读:
    clip-path属性深入理解与使用
    Django更换数据库和迁移数据方案
    后端系统开发注意事项
    区块链国际会议汇总
    dalle:zero-shot text-to-image generation
    “AI大模型革新浪潮:农业、生态、气象科研的新智能助手,开启智慧科研新篇章!“
    C# 优雅的处理TCP数据(心跳,超时,粘包断包,SSL加密 ,数据处理等)
    Lua中如何实现类似gdb的断点调试—06断点行号检查与自动修正
    醒悟的日子,我是怎么一步一步走向软件测试的道路
    内网渗透之内网信息收集(六)
  • 原文地址:https://blog.csdn.net/Huxiao1220/article/details/133469470