• 2023/10/1 -- ARM


    今日任务:select实现服务器并发

    ser.c:

    1. #include <myhead.h>
    2. #define ERR_MSG(msg) do{\
    3. printf("%d\n",__LINE__);\
    4. perror(msg);\
    5. }while(0)
    6. #define PORT 8888
    7. #define IP "192.168.1.5"
    8. int main(int argc, const char *argv[])
    9. {
    10. //创建流式套接字 socket
    11. int sfd = socket(AF_INET,SOCK_STREAM,0);
    12. if(sfd == -1){
    13. ERR_MSG("socket");
    14. return -1;
    15. }
    16. printf("socket create success! sfd = %d\n",sfd);
    17. //允许端口快速的被复用
    18. int reuse = 1;
    19. if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    20. {
    21. ERR_MSG("setsockopt");
    22. return -1;
    23. }
    24. printf("允许端口快速的被复用成功\n");
    25. //绑定服务器的地址信息 --->必须绑定 bind
    26. struct sockaddr_in sin;
    27. sin.sin_family = AF_INET;
    28. sin.sin_port = htons(PORT);
    29. sin.sin_addr.s_addr = inet_addr(IP);
    30. if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) == -1){
    31. ERR_MSG("bind");
    32. return -1;
    33. }
    34. printf("bind success!\n");
    35. //将套接字设置为被动监听状态 listen
    36. if(listen(sfd,128) == -1){
    37. ERR_MSG("listen");
    38. return -1;
    39. }
    40. printf("listen success!\n");
    41. fd_set readfds,tempfds;
    42. //清空集合
    43. FD_ZERO(&readfds);
    44. //添加文件描述符
    45. FD_SET(0,&readfds);
    46. FD_SET(sfd,&readfds);
    47. struct sockaddr_in cin;
    48. socklen_t addrlen = sizeof(cin);
    49. int newfd = -1;
    50. char buf[32];
    51. ssize_t res = 0;
    52. int s_res = -1;
    53. int maxfd = sfd;
    54. int sndfd = -1;
    55. struct sockaddr_in save[1024];
    56. while(1){
    57. tempfds = readfds;
    58. maxfd = maxfd>newfd?maxfd:newfd;
    59. s_res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
    60. if(s_res == -1){
    61. ERR_MSG("select");
    62. return -1;
    63. }else if(s_res == 0){
    64. printf("time out\n");
    65. break;
    66. }
    67. for(int i = 0;i <= maxfd;i++){
    68. if(FD_ISSET(i,&tempfds) == 0)
    69. continue;
    70. if(0 == i){
    71. printf("触发键盘输入事件\n");
    72. bzero(buf,sizeof(buf));
    73. int ret = scanf("%d %s",&sndfd,buf);
    74. if(ret != 2){
    75. printf("您的输入格式有误:sndfd buf\n");
    76. return -1;
    77. }
    78. if(sndfd <= 2 || FD_ISSET(sndfd,&readfds) == 0){
    79. printf("您输入的文件描述符不合法 snfd = %d\n",sndfd);
    80. return -1;
    81. }
    82. //发送
    83. if(send(sndfd,buf,sizeof(buf),0) == -1){
    84. ERR_MSG("send");
    85. return -1;
    86. }
    87. printf("send success!\n");
    88. }else if(sfd == i){
    89. printf("触发客户端连接事件\n");
    90. //获取一个已经完成的客户端信息,生成一个新的文件描述符 accept
    91. newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen);
    92. if(newfd == -1){
    93. ERR_MSG("accept");
    94. return -1;
    95. }
    96. printf("[%s:%d]客户端链接成功! newfd = %d\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
    97. save[newfd] = cin;
    98. FD_SET(newfd,&readfds);
    99. }else{
    100. printf("触发接收客户端数据事件\n");
    101. //接收
    102. bzero(buf,sizeof(buf));
    103. res = recv(i,buf,sizeof(buf),0);
    104. if(res == -1){
    105. ERR_MSG("recv");
    106. return -1;
    107. }else if(res == 0){
    108. printf("[%s:%d]客户端已下线! newfd = %d\n",inet_ntoa(save[i].sin_addr),ntohs(save[i].sin_port),i);
    109. close(i);
    110. FD_CLR(i,&readfds);
    111. while(FD_ISSET(maxfd,&readfds) == 0 && maxfd-->=0);
    112. continue;
    113. }
    114. printf("[%s:%d] newfd = %d buf = %s \n",inet_ntoa(save[i].sin_addr),ntohs(save[i].sin_port),i,buf);
    115. //发送
    116. if(send(i,buf,sizeof(buf),0) == -1){
    117. ERR_MSG("send");
    118. return -1;
    119. }
    120. printf("send success!\n");
    121. }
    122. }
    123. }
    124. //关闭文件描述符
    125. if(close(sfd)<0){
    126. ERR_MSG("close");
    127. return -1;
    128. }
    129. if(close(newfd) < 0){
    130. ERR_MSG("close");
    131. return -1;
    132. }
    133. return 0;
    134. }

    效果图:

  • 相关阅读:
    Java替换RequestBody和RequestParam参数的属性
    语法练习:sum_double
    Oracle 锁表,如何解锁
    分布式文件系统FastDFS 技术整理
    在Visual Studio Code macOS上尽量用Clang编译C++
    华为eNSP配置专题-VRRP的配置
    el-cascader 根据 已知数据 子节点的id 获取对应的所有父节点id
    两个部门,六轮面试,终与字节无缘
    Redis是否要分库的实践
    麻了,不要再动不动就BeanUtil.copyProperties!!!
  • 原文地址:https://blog.csdn.net/weixin_54147737/article/details/133460250