• 10.1 国庆节小任务


    目录

    select实现服务器并发

    服务器

    客户端

    运行现象


    select实现服务器并发

    服务器

    1. #include
    2. #define PORT 8888 //1024~49151
    3. #define IP "192.168.1.104" //ifconfig查看本机IP
    4. int main(int argc, const char *argv[])
    5. {
    6. //创建流式套接字
    7. int sfd = socket(AF_INET, SOCK_STREAM, 0);
    8. if(sfd < 0)
    9. {
    10. ERR_MSG("socket");
    11. return -1;
    12. }
    13. printf("sfd = %d\n", sfd);
    14. //设置允许端口被快速复用
    15. int reuse = 1;
    16. if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    17. {
    18. ERR_MSG("setsockopt");
    19. return -1;
    20. }
    21. printf("允许端口快速重用成功\n");
    22. //填充地址信息结构体,真实的地址信息结构体根据地址族指定
    23. //AF_INET: man 7 ip
    24. struct sockaddr_in sin;
    25. sin.sin_family = AF_INET; //必须填AF_INET
    26. sin.sin_port = htons(PORT); //端口号:1024~49151
    27. sin.sin_addr.s_addr = inet_addr(IP); //本机IP ifconfig查看
    28. //绑定服务器的IP和端口--->必须绑定
    29. if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
    30. {
    31. ERR_MSG("bind");
    32. return -1;
    33. }
    34. printf("bind success \n");
    35. //将套接字设置为被动监听状态
    36. if(listen(sfd, 128) < 0)
    37. {
    38. ERR_MSG("listen");
    39. return -1;
    40. }
    41. printf("listen success\n");
    42. //创建一个读集合
    43. fd_set readfds, tempfds;
    44. //fd_set本质上是一个结构体,成员是一个long类型数组,
    45. //集合目的是为了存储让内核监测的文件描述符,若不初始化,
    46. //则有可能随机到有效的但不需要监测的文件描述符,
    47. //所以需要将集合初始化成无意义的文件描述符
    48. FD_ZERO(&readfds);
    49. FD_ZERO(&tempfds);
    50. //将需要的文件描述符添加到读集合中
    51. FD_SET(0, &readfds);
    52. FD_SET(sfd, &readfds);
    53. int maxfd = sfd;
    54. int s_res = -1;
    55. int newfd = -1;
    56. char buf[128] = "";
    57. ssize_t res = 0;
    58. struct sockaddr_in cin; //存储客户端的地址信息
    59. socklen_t addrlen = sizeof(cin);
    60. struct sockaddr_in saveCin[1024-3];
    61. while(1)
    62. {
    63. tempfds = readfds;
    64. //调用IO多路复用函数 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. //printf("__%d__\n", __LINE__);
    77. //能运行到当前位置,则代表集合中有文件描述符准备就绪。
    78. //此时集合中会只剩下触发事件的文件描述符
    79. //例如:
    80. //当0号文件描述符触发事件,则集合中只剩下0
    81. //当sfd文件描述符触发事件,则集合中只剩下sfd
    82. //若0和sfd同时触发事件,则集合中剩下0和sfd
    83. //判断是哪个文件描述符准备就绪,走对应处理函数即可
    84. //判断集合中剩下哪个文件描述符,就代表哪个文件描述符准备就绪
    85. for(int i=0; i<=maxfd; i++)
    86. {
    87. if(FD_ISSET(i, &tempfds) == 0) //若i所代表的文件描述符不在集合中
    88. continue; //则继续遍历下一个i
    89. //能运行到当前位置,说明i所代表的文件描述符在集合中
    90. //判断i所代表的文件描述符触发的事件是什么事件
    91. if(0 == i)
    92. {
    93. printf("触发键盘输入事件\n");
    94. int sndfd = -1;
    95. res = scanf("%d %s", &sndfd,buf); //返回值:成功获取到的数据个数
    96. while(getchar() != 10);
    97. if(res != 2){
    98. printf("请输入正确的格式: fd string\n");
    99. continue;
    100. }
    101. //能运行到当前位置,则代表输入的格式正确
    102. if(sndfd<3 || !FD_ISSET(sndfd, &readfds))
    103. {
    104. printf("sndfd=%d 文件描述符错误\n", sndfd);
    105. continue;
    106. }
    107. if(send(sndfd, buf, sizeof(buf), 0) < 0)
    108. {
    109. ERR_MSG("send");
    110. return -1;
    111. }
    112. printf("send success\n");
    113. }
    114. else if(sfd == i)
    115. {
    116. printf("触发客户端连接事件\n");
    117. newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);
    118. if(newfd < 0)
    119. {
    120. ERR_MSG("accept");
    121. return -1;
    122. }
    123. printf("[%s : %d] newfd=%d 客户端连接成功\n", \
    124. inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd);
    125. saveCin[newfd-3] = cin;
    126. //将newfd添加到集合中
    127. FD_SET(newfd, &readfds);
    128. //更新maxfd
    129. maxfd = maxfd>newfd ? maxfd:newfd;
    130. }
    131. else
    132. {
    133. printf("触发客户端交互事件\n");
    134. bzero(buf, sizeof(buf));
    135. //接收数据
    136. res = recv(i, buf, sizeof(buf), 0);
    137. if(res < 0)
    138. {
    139. ERR_MSG("recv");
    140. return -1;
    141. }
    142. else if(0 == res)
    143. {
    144. printf("[%s : %d] newfd=%d 客户端下线\n", \
    145. inet_ntoa(saveCin[i-3].sin_addr), ntohs(saveCin[i-3].sin_port), i);
    146. //关闭下线客户端的文件描述符
    147. close(i);
    148. //将下线客户端的文件描述符从集合中剔除
    149. FD_CLR(i, &readfds);
    150. //更新maxfd
    151. //从大往小判断--->从目前记录的最大fd开始判断是否在集合中
    152. //若存在,则该文件描述符就是最大的,
    153. //若不存在,则继续往小判断
    154. while(FD_ISSET(maxfd, &readfds)==0 && maxfd-->=0);
    155. /*
    156. int j = maxfd;
    157. for(; j>=0; j--)
    158. {
    159. if(FD_ISSET(j, &readfds))
    160. break;
    161. }
    162. maxfd = j;
    163. */
    164. continue;
    165. }
    166. printf("[%s : %d] newfd=%d : %s\n", \
    167. inet_ntoa(saveCin[i-3].sin_addr), ntohs(saveCin[i-3].sin_port), i, buf);
    168. //发送数据
    169. strcat(buf, "*_*");
    170. if(send(i, buf, sizeof(buf), 0) < 0)
    171. {
    172. ERR_MSG("send");
    173. return -1;
    174. }
    175. printf("send success\n");
    176. }
    177. }
    178. }
    179. //关闭所有文件描述符
    180. close(sfd);
    181. return 0;
    182. }

    客户端

    1. #include
    2. #define PORT 8888 //1024~49151
    3. #define IP "192.168.1.104"
    4. int main(int argc, const char *argv[])
    5. {
    6. //创建流失套接字
    7. int cfd=socket(AF_INET,SOCK_STREAM,0);
    8. if(cfd<0)
    9. {
    10. ERR_MSG("socket");
    11. return -1;
    12. }
    13. //填充服务器的ip地址信息结构体
    14. struct sockaddr_in sin;
    15. sin.sin_family=AF_INET;
    16. sin.sin_port=htons(PORT);
    17. sin.sin_addr.s_addr=inet_addr(IP);
    18. //连接服务器
    19. if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))<0)
    20. {
    21. ERR_MSG("connect");
    22. return -1;
    23. }
    24. printf("连接服务器成功\n");
    25. char buf[128];
    26. //与服务器交互
    27. while(1)
    28. {
    29. bzero(buf,sizeof(buf));
    30. fgets(buf,sizeof(buf),stdin);
    31. buf[strlen(buf)-1]='\0';
    32. if(send(cfd,buf,sizeof(buf),0)<0)
    33. {
    34. ERR_MSG("send");
    35. return -1;
    36. }
    37. if(0==strlen(buf))
    38. {
    39. break;
    40. }
    41. printf("向服务器发送消息成功\n");
    42. bzero(buf,sizeof(buf));
    43. if(recv(cfd,buf,sizeof(buf),0)<0)
    44. {
    45. ERR_MSG("recv");
    46. return -1;
    47. }
    48. printf("服务器:%s\n",buf);
    49. }
    50. //关闭客户端
    51. close(cfd);
    52. return 0;
    53. }

    运行现象

  • 相关阅读:
    vue实现防抖函数、节流函数,全局使用【输入框、按钮】
    常见的css面试题(持续更新,欢迎补充)
    多商户商城系统功能拆解16讲-平台端会员成长值记录
    代码随想录算法训练营第三十二天 | LeetCode 122. 买卖股票的最佳时机 II、55. 跳跃游戏、45. 跳跃游戏 II
    Pipelines in Shell
    10分钟学会Hive之用户自定义函数UTF开发
    RHCSA之Linux基础
    解决跨域的三种方案
    excel导出加水印内存溢出问题解决思路
    判断二叉树是否为满二叉树
  • 原文地址:https://blog.csdn.net/weixin_58469613/article/details/133460861