• io+day5


    1,select服务端

    1. 1 #include
    2. 2
    3. 3 #define PORT 8888 //端口号
    4. 4 #define IP "192.168.228.165" //IP地址
    5. 5
    6. 6
    7. 7 int main(int argc, const char *argv[])
    8. 8 {
    9. 9 //1、创建用于接受连接的套接字
    10. 10 int sfd = socket(AF_INET, SOCK_STREAM, 0);
    11. 11 if(sfd == -1)
    12. 12 {
    13. 13 perror("socket error");
    14. 14 return -1;
    15. 15 }
    16. 16
    17. 17 printf("socket success sfd = %d\n", sfd); //4
    18. 18
    19. 19
    20. 20 //设置端口号快速重用
    21. 21 int reuse = 1;
    22. 22 if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
    23. 23 {
    24. 24 perror("setsockopt error");
    25. 25 return -1;
    26. 26 }
    27. 27 printf("设置端口快速重用成功 _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33 //2、绑定IP地址和端口号
    34. 34 //2.1、填充要绑定的地址信息结构体
    35. 35 struct sockaddr_in sin;
    36. 36 sin.sin_family = AF_INET; //表明是ipv4
    37. 37 sin.sin_port = htons(PORT); //端口号
    38. 38 sin.sin_addr.s_addr = inet_addr(IP); //IP地址
    39. 39
    40. 40 //2.2、绑定
    41. 41 if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin))==-1)
    42. 42 {
    43. 43 perror("bind error");
    44. 44 return -1;
    45. 45 }
    46. 46 printf("bind success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);
    47. 47
    48. 48 //3、将套接字设置成被动监听状态
    49. 49 if(listen(sfd, 128) == -1)
    50. 50 {
    51. 51 perror("listen error");
    52. 52 return -1;
    53. 53 }
    54. 54
    55. 55 printf("listen success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);
    56. 56
    57. 57 //4、阻塞等待客户端连接请求,如果有新的客户端连接,则创建一个新的用于通信的套接字
    58. 58 //4.1、定义客户端地址信息结构体
    59. 59 struct sockaddr_in cin; //客户端地址信息结构体
    60. 60 cin.sin_family = AF_INET;
    61. 61 socklen_t socklen = sizeof(cin); //客户端地址信息的大小
    62. 62
    63. 63
    64. 64 定义一个用于检测文件描述符的集合
    65. 65 fd_set readfds, tempfds; //在栈区定义
    66. 66
    67. 67 清空容器中的内容
    68. 68 FD_ZERO(&readfds);
    69. 69 将要检测的文件描述符放入集合中
    70. 70 FD_SET(sfd, &readfds); //将sfd文件描述符放入
    71. 71 FD_SET(0, &readfds); //将0号文件描述符放入
    72. 72
    73. 73
    74. 74
    75. 75 //定义一个容器
    76. 76 char buf[128] = "";
    77. 77 int res = 0; //接收select的返回值
    78. 78 int newfd = -1; //存放用于最新连接客户端的套接字
    79. 79 int maxfd = sfd; //定义控制select函数中最大文件描述符
    80. 80
    81. 81 struct sockaddr_in saveCin[1024]; //用于存放客户端地址信息结构体
    82. 82
    83. 83
    84. 84 while(1)
    85. 85 {
    86. 86 将集合内容复制一份
    87. 87 tempfds = readfds;
    88. 88
    89. 89 使用select阻塞等待集合中的文件描述符有事件产生
    90. 90 res = select(maxfd+1, &tempfds, NULL, NULL, NULL);
    91. 91 if(res == -1)
    92. 92 {
    93. 93 perror("select error");
    94. 94 return -1;
    95. 95 }else if(res == 0)
    96. 96 {
    97. 97 printf("time out\n");
    98. 98 return -1;
    99. 99 }
    100. 100
    101. 101
    102. 102 //遍历所有集合中文件描述符
    103. 103 for(int i=0; i<=maxfd; i++)
    104. 104 {
    105. 105 //判断当前i是否在集合中,如果不在,直接判断下一个
    106. 106 if(!FD_ISSET(i, &tempfds))
    107. 107 {
    108. 108 continue;
    109. 109 }
    110. 110
    111. 111 判断sfd是否还在集合中
    112. 112 if( i == sfd)
    113. 113 {
    114. 114 //4.2、阻塞接收客户端的链接请求,并且获取客户端的地址信息
    115. 115 newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);
    116. 116 if(newfd == -1)
    117. 117 {
    118. 118 perror("accept error");
    119. 119 return -1;
    120. 120 }
    121. 121 printf("accept success _%d_ %s_ %s_\n", __LINE__, __FILE__, __func__);
    122. 122
    123. 123 将newfd放入readfds中
    124. 124 FD_SET(newfd , &readfds);
    125. 125
    126. 126 //更新maxfd
    127. 127 if(newfd > maxfd)
    128. 128 {
    129. 129 maxfd = newfd;
    130. 130 }
    131. 131
    132. 132 //将最新的客户端套接字放入数组的下标为new的位置
    133. 133 saveCin[newfd] = cin;
    134. 134 printf("newfd = %d\n", newfd);
    135. 135
    136. 136 }else if(i == 0 ) //判断是否是终端输入
    137. 137
    138. 138 {
    139. 139 char buf1[1000] = "";
    140. 140
    141. 141 bzero(buf, sizeof(buf));
    142. 142 //从终端获取数据
    143. 143 fgets(buf, sizeof(buf), stdin); //从终端获取数据
    144. 144 buf[strlen(buf)-1]='\0';
    145. 145 printf("触发终端输入事件:%s\n", buf);
    146. 146
    147. 147 sprintf(buf1, "%s%s", "系统消息:", buf);
    148. 148
    149. 149 //将数据发送给所有客户端
    150. 150 for(int j=4; j<=maxfd; j++)
    151. 151 {
    152. 152 send(j, buf1,sizeof(buf1), 0);
    153. 153 }
    154. 154
    155. 155
    156. 156 }else
    157. 157 {
    158. 158 //5、收发数据使用newfd完成通信
    159. 159 char buf[128] = "";
    160. 160 //清空字符串
    161. 161 bzero(buf, sizeof(buf));
    162. 162 int ret = recv(i, buf, sizeof(buf), 0); //从套接字中读取客户端发来的消息
    163. 163
    164. 164 //判断收到的结果
    165. 165 if(ret == 0)
    166. 166 {
    167. 167 printf("客户端已经下线\n");
    168. 168 close(i); //关闭通信的套接字
    169. 169
    170. 170 将当前的文件描述符从集合中删除
    171. 171 FD_CLR(i, &readfds);
    172. 172
    173. 173 更新maxfd
    174. 174 for(int j=maxfd; j>=0; j--)
    175. 175 {
    176. 176 //判断当前的j是否在集合中,如果在,则为maxfd
    177. 177 if(FD_ISSET(j, &readfds))
    178. 178 {
    179. 179 maxfd = j;
    180. 180 break;
    181. 181 }
    182. 182 }
    183. 183
    184. 184 continue; //继续判断下一个
    185. 185 }else if(ret < 0)
    186. 186 {
    187. 187 perror("recv error");
    188. 188 return -1;
    189. 189 }
    190. 190
    191. 191 printf("[%s:%d]:%s\n", inet_ntoa(saveCin[i].sin_addr), ntohs(saveCin[i].sin_port), buf);
    192. 192
    193. 193 //将读取的信息,加上一些字符发送回去
    194. 194 strcat(buf, "*_*");
    195. 195 send(i, buf, sizeof(buf), 0);
    196. 196
    197. 197
    198. 198 }
    199. 199 }
    200. 200
    201. 201 }
    202. 202
    203. 203
    204. 204
    205. 205 //6、关闭所有套接字
    206. 206 close(sfd); //关闭监听
    207. 207
    208. 208 return 0;
    209. 209 }
    210. ~
    1. #include
    2. 2
    3. 3 #define SERPORT 8888 //服务器端口号
    4. 4 #define SERIP "192.168.228.165" //服务器IP地址
    5. 5
    6. 6 int main(int argc, const char *argv[])
    7. 7 {
    8. 8 //创建用于通信的套接字
    9. 9 int cfd = socket(AF_INET,SOCK_STREAM,0);
    10. 10 if(cfd == -1)
    11. 11 {
    12. 12 perror("socket error");
    13. 13 return -1;
    14. 14 }
    15. 15
    16. 16 //连接服务器
    17. 17 ///填充服务器地址信息结构体
    18. 18 struct sockaddr_in sin;
    19. 19 sin.sin_family = AF_INET;
    20. 20 sin.sin_port = htons(SERPORT);
    21. 21 sin.sin_addr.s_addr = inet_addr(SERIP);
    22. 22
    23. 23 ///连接服务器
    24. 24 if(connect(cfd,(struct sockaddr *)&sin,sizeof(sin)) == -1)
    25. 25 {
    26. 26 perror("connect error");
    27. 27 return -1;
    28. 28 }
    29. 29
    30. 30 //创建用于检测文件描述符的集合
    31. 31 fd_set readfds,tempfds;
    32. 32
    33. 33 //清空集合
    34. 34 FD_ZERO(&readfds);
    35. 35
    36. 36 //将要检测的文件描述符放入集合中
    37. 37 FD_SET(cfd,&readfds);
    38. 38 FD_SET(0,&readfds);
    39. 39
    40. 40 int res = 0; //接收select的返回值
    41. 41 int maxfd = cfd; //集合中值最大的文件描述符
    42. 42
    43. 43 //向服务器进行数据的收发
    44. 44 char buf[128] = "";
    45. 45 int ret = 0; //接收recv的返回值
    46. 46 while(1)
    47. 47 {
    48. 48 tempfds = readfds;
    49. 49
    50. 50 res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
    51. 51 if(res == -1)
    52. 52 {
    53. 53 perror("select error");
    54. 54 return -1;
    55. 55 }else if(res == 0)
    56. 56 {
    57. 57 printf("time out\n");
    58. 58 return -1;
    59. 59 }
    60. 60
    61. 61 //遍历集合中所有的文件描述符
    62. 62 for(int i = 0;i <= maxfd;i++)
    63. 63 {
    64. 64 //判断当前文件描述符是否在集合中
    65. 65 if(!FD_ISSET(i,&readfds))
    66. 66 {
    67. 67 continue;
    68. 68 }
    69. 69
    70. 70
    71. 71 //判断0号文件描述符是否还在集合中
    72. 72 if(0 == i)
    73. 73 {
    74. 74 //从标准输入中读取数据
    75. 75 fgets(buf,sizeof(buf),stdin);
    76. 76 buf[strlen(buf)-1] == 0;
    77. 77
    78. 78 //将数据发送到服务器
    79. 79 if(send(cfd,buf,sizeof(buf),0) == -1)
    80. 80 {
    81. 81 perror("send error");
    82. 82 return -1;
    83. 83 }
    84. 84
    85. 85 }else if(cfd == i) //判断cfd是否还在集合中
    86. 86 {
    87. 87 //接收来自服务器的消息
    88. 88 ret = recv(cfd,buf,sizeof(buf),0);
    89. 89 if(ret == -1)
    90. 90 {
    91. 91 perror("recv error");
    92. 92 return -1;
    93. 93 }else if(ret == 0)
    94. 94 {
    95. 95 printf("服务器已关闭\n");
    96. 96 return -1;
    97. 97 }
    98. 98
    99. 99 printf("服务器消息:%s\n",buf);
    100. 100 }
    101. 101 }
    102. 102 }
    103. 103
    104. 104 //关闭文件描述符
    105. 105 close(cfd);
    106. 106
    107. 107 return 0;
    108. 108 }

    效果图

    poll客户端

    1. 1 #include
    2. 2
    3. 3 #define IP "192.168.228.165"
    4. 4 #define PORT 8888
    5. 5
    6. 6 int main(int argc, const char *argv[])
    7. 7 {
    8. 8 //创建用于连接的套接字
    9. 9 int sfd = socket(AF_INET,SOCK_STREAM,0);
    10. 10 if(sfd == -1)
    11. 11 {
    12. 12 perror("socket error");
    13. 13 return -1;
    14. 14 }
    15. 15
    16. 16 //绑定服务器IP和端口号
    17. 17 ///填充服务器地址信息结构体
    18. 18 struct sockaddr_in sin;
    19. 19 sin.sin_family = AF_INET;
    20. 20 sin.sin_port = htons(PORT);
    21. 21 sin.sin_addr.s_addr = inet_addr(IP);
    22. 22
    23. 23 ///绑定
    24. 24 if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin)) == -1)
    25. 25 {
    26. 26 perror("bind error");
    27. 27 return -1;
    28. 28 }
    29. 29 printf("bind success\n");
    30. 30
    31. 31 //将连接用套接字设置为被动监听状态
    32. 32 if(listen(sfd,128) == -1)
    33. 33 {
    34. 34 perror("listen error");
    35. 35 return -1;
    36. 36 }
    37. 37 printf("listen success\n");
    38. 38
    39. 39 //定义一个集合管理sfd和打开的通信用文件描述符
    40. 40 struct pollfd fds[1024];
    41. 41 int maxfd = 0;
    42. 42
    43. 43
    44. 44 //手动放入sfd
    45. 45 fds[0].fd = sfd;
    46. 46 fds[0].events = POLLIN; //表明为读事件
    47. 47
    48. 48 //将fds中其余元素初始化为-1
    49. 49 for(int i = 4;i <= 1024;i++)
    50. 50 {
    51. 51 fds[i].fd = -1;
    52. 52 }
    53. 53
    54. 54 //填充客户端地址信息结构体
    55. 55 struct sockaddr_in cin;
    56. 56 cin.sin_family = AF_INET;
    57. 57 socklen_t socklen = sizeof(cin);
    58. 58
    59. 59
    60. 60 char cbuf[128] = ""; //给客户端用的容器
    61. 61 int nfd;
    62. 62 int res = 0; //接收poll返回的结果
    63. 63 while(1)
    64. 64 {
    65. 65 res = poll(fds,maxfd+1,-1);
    66. 66 if(res == -1)
    67. 67 {
    68. 68 perror("select");
    69. 69 return -1;
    70. 70 }
    71. 71 else if(res == 0)
    72. 72 {
    73. 73 continue;;
    74. 74 }
    75. 75 else if(res > 0) //说明检测到了有文件描述符对应的缓冲区的数据发生了改变
    76. 76 {
    77. 77 if(fds[0].revents == POLLIN) //表明有新的客户连接进来了
    78. 78 {
    79. 79 int nfd = accept(sfd,(struct sockaddr*)&cin,&socklen); //阻塞在此处,直到有客户端连接上来
    80. 80 if(nfd == -1) //增加这些错误的判断非常重要,可以帮助找到出现问题的地方
    81. 81 {
    82. 82 perror("accept");
    83. 83 return -1;
    84. 84 }
    85. 85
    86. 86 //将新的文件描述符加入到集合中
    87. 87 for(int i = 1;i < 1024;i++)
    88. 88 {
    89. 89 if( fds[i].fd == -1)
    90. 90 {
    91. 91 fds[i].fd = nfd;
    92. 92 fds[i].events = POLLIN;
    93. 93 break;
    94. 94 }
    95. 95 }
    96. 96
    97. 97 //更新最大的文件描述符
    98. 98 if(nfd > maxfd)
    99. 99 {
    100. 100 maxfd = nfd;
    101. 101 }
    102. 102 }
    103. 103
    104. 104 for(int i = 1;i <= maxfd;i++) //轮询客户端对应的文件描述符
    105. 105 {
    106. 106 if(fds[i].revents == POLLIN) //说明此文件描述符对应的客户端发送来了数据
    107. 107 {
    108. 108 int ret = read(fds[i].fd,cbuf,sizeof(cbuf));
    109. 109 if(ret == -1)
    110. 110 {
    111. 111 perror("read");
    112. 112 exit(-1);
    113. 113 }
    114. 114 else if(ret == 0)
    115. 115 {
    116. 116 printf("client closed\n");
    117. 117 close(fds[i].fd); //关闭对应的文件描述符
    118. 118 fds[i].fd = -1; //在fds中清空对应的文件描述符
    119. 119 }
    120. 120 else if(ret > 0)
    121. 121 {
    122. 122 printf("read buf = %s\n",cbuf);
    123. 123 write(fds[i].fd,cbuf,strlen(cbuf)+1);
    124. 124 }
    125. 125
    126. 126
    127. 127 }
    128. 128 }
    129. 129 }
    130. 130 }
    131. 131 //关闭所有套接字
    132. 132 close(sfd);
    133. 133
    134. 134 return 0;
    135. 135
    1. 1 #include
    2. 2 #define SERIP "192.168.228.165"
    3. 3 #define SERPORT 8888
    4. 4 #define CLIIP "192.168.228.165"
    5. 5 #define CLIPORT 6666
    6. 6
    7. 7 int main(int argc, const char *argv[])
    8. 8 {
    9. 9 //1、创建客户端用于通信的套接字
    10. 10 int cfd = socket(AF_INET, SOCK_STREAM, 0);
    11. 11 if(cfd == -1)
    12. 12 {
    13. 13 perror("socket error");
    14. 14 return -1;
    15. 15 }
    16. 16 printf("cfd = %d\n", cfd); //3
    17. 17
    18. 18
    19. 19 //2、绑定(可选)
    20. 20 //2.1、填充地址信息结构体
    21. 21 struct sockaddr_in cin;
    22. 22 cin.sin_family = AF_INET; //使用的是ipv4通信
    23. 23 cin.sin_port = htons(CLIPORT); //服务器端口号
    24. 24 cin.sin_addr.s_addr = inet_addr(CLIIP); //服务器IP地址
    25. 25 //2.2、绑定工作
    26. 26 if(bind(cfd, (struct sockaddr*)&cin, sizeof(cin)) == -1)
    27. 27 {
    28. 28 perror("bind error");
    29. 29 return -1;
    30. 30 }
    31. 31 printf("bind success\n");
    32. 32
    33. 33
    34. 34 //3、连接服务器
    35. 35 //3.1、填充服务器地址信息结构体
    36. 36 struct sockaddr_in sin;
    37. 37 sin.sin_family = AF_INET; //使用的是ipv4通信
    38. 38 sin.sin_port = htons(SERPORT); //服务器端口号
    39. 39 sin.sin_addr.s_addr = inet_addr(SERIP); //服务器IP地址
    40. 40
    41. 41 //3.2、连接服务器
    42. 42 if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) ==-1)
    43. 43 {
    44. 44 perror("connect error");
    45. 45 return -1;
    46. 46 }
    47. 47 printf("connect success\n");
    48. 48
    49. 49 //4、收发数据(send、recv、read、write)
    50. 50 char buf[128] = "";
    51. 51 char rbuf[128] = "";
    52. 52
    53. 53 定义一个集合管理0号文件描述符和cfd
    54. 54 struct pollfd fds[2];
    55. 55
    56. 56 //将0号文件描述符放入
    57. 57 fds[0].fd = 0;
    58. 58 fds[0].events = POLLIN; //表明要进行读事件
    59. 59
    60. 60 //将cfd放入集合
    61. 61 fds[1].fd = cfd;
    62. 62 fds[1].events = POLLIN;
    63. 63
    64. 64 int res = 0; //接收poll返回的结果
    65. 65
    66. 66
    67. 67 while(1)
    68. 68 {
    69. 69 res = poll(fds, 2, -1); //第三个参数如果是负数,表明一直等待
    70. 70 if(res == -1)
    71. 71 {
    72. 72 perror("poll error");
    73. 73 return -1;
    74. 74 }else if(res == 0)
    75. 75 {
    76. 76 printf("time out\n");
    77. 77 return -1;
    78. 78 }
    79. 79
    80. 80
    81. 81 bzero(buf, sizeof(buf));
    82. 82 bzero(rbuf, sizeof(rbuf));
    83. 83
    84. 84
    85. 85
    86. 86 //判断是否是发送数据满足条件
    87. 87 if(fds[0].revents == POLLIN)
    88. 88 {
    89. 89 fgets(buf, sizeof(buf), stdin); //从标准输入中读取数据
    90. 90 buf[strlen(buf)-1] = '\0';
    91. 91
    92. 92 //将数据发送给服务器
    93. 93 send(cfd, buf, sizeof(buf), 0);
    94. 94
    95. 95 //如果输入的是quit则退出
    96. 96 if(strcmp(buf,"quit") == 0)
    97. 97 {
    98. 98 break;
    99. 99 }
    100. 100 }
    101. 101
    102. 102
    103. 103
    104. 104 //判断是否为接收数据满足条件
    105. 105 if(fds[1].revents == POLLIN)
    106. 106 {
    107. 107
    108. 108 //接收服务器发送来的消息
    109. 109 int res = recv(cfd, rbuf, sizeof(rbuf), 0);
    110. 110 if(res == 0)
    111. 111 {
    112. 112 printf("服务器已经关闭\n");
    113. 113 break;
    114. 114 }
    115. 115 printf("rbuf = %s\n", rbuf);
    116. 116 }
    117. 117
    118. 118 }
    119. 119
    120. 120 //5、关闭客户端套接字
    121. 121 close(cfd);
    122. 122
    123. 123
    124. 124 return 0;
    125. 125 }
    126. 126
    127. ~

  • 相关阅读:
    VUE基础的一些实战总结
    通过bat脚本控制Oracle服务启动停止
    人工神经网络与遗传算法,神经网络和算法的关系
    快递如何查物流,这几种方法都不错
    360crawlergo结合xray被动扫描
    从混合云到分布式云 (上篇)
    Django系列8-员工管理系统实战--部门管理
    【第0天】SQL快速入门-安装MYSQL环境(SQL 小虚竹)
    2023-9-27 JZ77 按之字型顺序打印二叉树
    苹果Siri怎么打开?教你两招轻松唤醒!
  • 原文地址:https://blog.csdn.net/ZMH12345_/article/details/134564066