• hwk2:select实现服务器并发


    selectTcpSer.c:

    1. #include <myhead.h>
    2. #define ERR_MSG(msg) do{\
    3. fprintf(stderr,"__%d__\n",__LINE__);\
    4. perror(msg);\
    5. }while(0)
    6. #define PORT 8888
    7. int keybroad_event(fd_set readfds)
    8. {
    9. char buf[128] = "";
    10. int sendfd = -1;
    11. bzero(buf,sizeof(buf));
    12. int res = scanf("%d %s",&sendfd,buf);
    13. while(getchar()!=10);
    14. if(res != 2)
    15. {
    16. printf("input error,usage:sendfd string\n");
    17. return -1;
    18. }
    19. if(sendfd <=2 || FD_ISSET(sendfd,&readfds) == 0)
    20. {
    21. printf("非法的文件描述符\n");
    22. return -1;
    23. }
    24. if(send(sendfd,buf,sizeof(buf),0) < 0)
    25. {
    26. ERR_MSG("send");
    27. return -1;
    28. }
    29. printf("send success\n");
    30. return 0;
    31. }
    32. int cliconnect_event(int sfd,struct sockaddr_in cliaddrs[],fd_set *preadfds,int *pmaxfd)
    33. {
    34. int newfd = -1; //客户端文件描述符
    35. struct sockaddr_in cin; //用来存储客户端地址信息
    36. socklen_t addrlen = sizeof(cin);
    37. newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen);
    38. if(newfd < 0)
    39. {
    40. ERR_MSG("accept");
    41. return -1;
    42. }
    43. printf("[%s:%d]客户端连接成功,newfd = %d\n",\
    44. inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
    45. cliaddrs[newfd]=cin; //将客户端地址信息保存到数组中
    46. FD_SET(newfd,preadfds); //将文件描述符添加到集合中
    47. *pmaxfd = *pmaxfd > newfd ? *pmaxfd:newfd; //更新maxfd
    48. return 0;
    49. }
    50. int clichange_event(int fd,struct sockaddr_in cliaddrs[],fd_set *preadfds,int *pmaxfd)
    51. {
    52. char buf[128] = "";
    53. bzero(buf,sizeof(buf));
    54. ssize_t ret = recv(fd,buf,sizeof(buf),0);
    55. if(ret < 0)
    56. {
    57. ERR_MSG("recv");
    58. return -1;
    59. }
    60. else if(0 == ret)
    61. {
    62. printf("[%s:%d]客户端下线 fd = %d",\
    63. inet_ntoa(cliaddrs[fd].sin_addr),ntohs(cliaddrs[fd].sin_port),fd);
    64. close(fd);
    65. FD_CLR(fd,preadfds);
    66. while(FD_ISSET(*pmaxfd,preadfds) == 0 && *pmaxfd-->=0);
    67. return 0;
    68. }
    69. printf("[%s:%d] newfd = %d:%s\n",\
    70. inet_ntoa(cliaddrs[fd].sin_addr),ntohs(ntohs(cliaddrs[fd].sin_port)),fd,buf);
    71. //发送
    72. strcat(buf,">-<");
    73. if(send(fd,buf,sizeof(buf),0) < 0)
    74. {
    75. ERR_MSG("send");
    76. return -1;
    77. }
    78. printf("send success\n");
    79. return 0;
    80. }
    81. int main(int argc, const char *argv[])
    82. {
    83. //判断输入合法性
    84. if(argc < 2)
    85. {
    86. printf("input error,usage:./a.out ip\n");
    87. return -1;
    88. }
    89. //创建流式套接字
    90. int sfd;
    91. if((sfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
    92. {
    93. ERR_MSG("socket");
    94. return -1;
    95. }
    96. printf("socket success sfd = %d\n",sfd);
    97. //允许端口快速复用
    98. int reuse = 1;
    99. if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
    100. {
    101. ERR_MSG("setsockopt");
    102. return -1;
    103. }
    104. //填写地址信息结构体给bind使用
    105. struct sockaddr_in sin;
    106. sin.sin_family = AF_INET;
    107. sin.sin_port = htons(PORT);
    108. sin.sin_addr.s_addr = inet_addr(argv[1]);
    109. //bind绑定地址信息,必须绑定
    110. if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
    111. {
    112. ERR_MSG("bind");
    113. return -1;
    114. }
    115. printf("bind success\n");
    116. //listen 将套接字转换成监听模式
    117. if(listen(sfd,128) < 0)
    118. {
    119. ERR_MSG("listen");
    120. return -1;
    121. }
    122. printf("listen success\n");
    123. //创建一个读集合
    124. fd_set readfds,tempfds;
    125. //清零
    126. FD_ZERO(&readfds);
    127. //将文件描述符添加到集合中
    128. FD_SET(0,&readfds);
    129. FD_SET(sfd,&readfds);
    130. int s_res = -1;
    131. ssize_t res = -1;
    132. int maxfd = sfd; //存储最大文件描述符
    133. struct sockaddr_in cliaddrs[1024]; //记录客户端信息
    134. while(1)
    135. {
    136. //用来额外备份,保证原先的集合不变
    137. tempfds = readfds;
    138. //实行IO多路复用
    139. s_res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
    140. if(s_res < 0)
    141. {
    142. ERR_MSG("select");
    143. return -1;
    144. }
    145. else if(s_res == 0)
    146. {
    147. printf("timeout\n");
    148. return -1;
    149. }
    150. //当有文件描述符准备就绪时,该集合中就会只剩下已经准备就绪的文件描述符
    151. for(int i=0;i<=maxfd;i++)
    152. {
    153. if(FD_ISSET(i,&tempfds) == 0)
    154. continue;
    155. //运行到这说明集合内有这个文件描述符
    156. if(0 == i)
    157. {
    158. //键盘输入事件
    159. keybroad_event(readfds);
    160. }else if(sfd == i)
    161. {
    162. //客户端连接事件
    163. cliconnect_event(sfd,cliaddrs,&readfds,&maxfd);
    164. }else{
    165. //客户端交互事件
    166. clichange_event(i,cliaddrs,&readfds,&maxfd);
    167. }
    168. }
    169. }
    170. //关闭套接字
    171. if(close(sfd) < 0)
    172. {
    173. ERR_MSG("close");
    174. return -1;
    175. }
    176. return 0;
    177. }

    效果图:

  • 相关阅读:
    基于Python机器学习实现的花卉识别
    【linux命令讲解大全】112.Linux 系统管理工具:dpkg-statoverride 和 dstat 的使用介绍
    SpringBoot3项目中配置JDK17
    基于ssm+vue员工工资管理系统
    在Go中处理异常
    设计模式详解(九)——桥接模式
    基于python的图像识别
    大量短信群发?不妨来看看这几个平台
    uni-app中使用computed解决了tab切换中data()值显示的异常
    GAMES101 路径追踪
  • 原文地址:https://blog.csdn.net/wxmchong/article/details/133469544