解决多客户端高并发的问题,通过非阻塞的模式去轮训多个clent客户端的发送内容,进行解析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
// // main.c // 震动台server // // Created by ea huang on 2016/7/19. // Copyright © 2018年 ea huang. All rights reserved. // #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/time.h> #define DEFAULT_PORT 1921 //默认端口 #define BUFF_SIZE 1024 //buffer大小 #define SELECT_TIMEOUT 5 //select的timeout seconds void parse_socket_buf(char * buf) { if(buf == NULL) { return ; } char delims[] = ","; char *result = NULL; while( (result = strtok( NULL, delims )) != NULL ) { printf( "result is \"%s\"\n", result ); } } //函数:设置sock为non-blocking mode void setSockNonBlock(int sock) { int flags; flags = fcntl(sock, F_GETFL, 0); if (flags < 0) { perror("fcntl(F_GETFL) failed"); exit(EXIT_FAILURE); } if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) { perror("fcntl(F_SETFL) failed"); exit(EXIT_FAILURE); } } //函数:更新maxfd int updateMaxfd(fd_set fds, int maxfd) { int i; int new_maxfd = 0; for (i = 0; i <= maxfd; i++) { if (FD_ISSET(i, &fds) && i > new_maxfd) { new_maxfd = i; } } return new_maxfd; } int main(int argc, char *argv[]) { unsigned short int port; //获取自定义端口 if (argc == 2) { port = atoi(argv[1]); } else if (argc < 2) { port = DEFAULT_PORT; } else { fprintf(stderr, "USAGE: %s [port]\n", argv[0]); exit(EXIT_FAILURE); } //创建socket int sock; if ( (sock = socket(PF_INET, SOCK_STREAM, 0)) == -1 ) { perror("socket failed, "); exit(EXIT_FAILURE); } printf("socket done\n"); //in case of 'address already in use' error message int yes = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))) { perror("setsockopt failed"); exit(EXIT_FAILURE); } //设置sock为non-blocking setSockNonBlock(sock); //创建要bind的socket address struct sockaddr_in bind_addr; memset(&bind_addr, 0, sizeof(bind_addr)); bind_addr.sin_family = AF_INET; bind_addr.sin_addr.s_addr = htonl(INADDR_ANY); //设置接受任意地址 bind_addr.sin_port = htons(port); //将host byte order转换为network byte order //bind sock到创建的socket address上 if ( bind(sock, (struct sockaddr *) &bind_addr, sizeof(bind_addr)) == -1 ) { perror("bind failed, "); exit(EXIT_FAILURE); } printf("bind done\n"); //listen if ( listen(sock, 20) == -1) { perror("listen failed."); exit(EXIT_FAILURE); } printf("listen done\n"); //创建并初始化select需要的参数(这里仅监视read),并把sock添加到fd_set中 fd_set readfds; fd_set readfds_bak; //backup for readfds(由于每次select之后会更新readfds,因此需要backup) struct timeval timeout; int maxfd; maxfd = sock; FD_ZERO(&readfds); FD_ZERO(&readfds_bak); FD_SET(sock, &readfds_bak); //循环接受client请求 int new_sock; struct sockaddr_in client_addr; socklen_t client_addr_len; char client_ip_str[INET_ADDRSTRLEN]; int res; int i; char buffer[BUFF_SIZE]; long int recv_size; while (1) { //注意select之后readfds和timeout的值都会被修改,因此每次都进行重置 readfds = readfds_bak; maxfd = updateMaxfd(readfds, maxfd); //更新maxfd timeout.tv_sec = SELECT_TIMEOUT; timeout.tv_usec = 0; printf("selecting maxfd=%d\n", maxfd); //select(这里没有设置writefds和errorfds,如有需要可以设置) res = select(maxfd + 1, &readfds, NULL, NULL, &timeout); if (res == -1) { perror("select failed"); exit(EXIT_FAILURE); } else if (res == 0) { fprintf(stderr, "no socket ready for read within %d secs\n", SELECT_TIMEOUT); continue; } //检查每个socket,并进行读(如果是sock则accept) for (i = 0; i <= maxfd; i++) { if (!FD_ISSET(i, &readfds)) { continue; } //可读的socket if ( i == sock) { //当前是server的socket,不进行读写而是accept新连接 client_addr_len = sizeof(client_addr); new_sock = accept(sock, (struct sockaddr *) &client_addr, &client_addr_len); if (new_sock == -1) { perror("accept failed"); exit(EXIT_FAILURE); } if (!inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip_str, sizeof(client_ip_str))) { perror("inet_ntop failed"); exit(EXIT_FAILURE); } printf("accept a client from: %s\n", client_ip_str); //设置new_sock为non-blocking setSockNonBlock(new_sock); //把new_sock添加到select的侦听中 if (new_sock > maxfd) { maxfd = new_sock; } FD_SET(new_sock, &readfds_bak); } else { //当前是client连接的socket,可以写(read from client) memset(buffer, 0, sizeof(buffer)); if ( (recv_size = recv(i, buffer, sizeof(buffer), 0)) == -1 ) { perror("recv failed"); if ( close(i) == -1 ) { perror("close failed"); exit(EXIT_FAILURE); } FD_CLR(i, &readfds_bak); } if((res == 1) && (recv_size == 0)) { //perror("send failed"); printf("res is %d but rcv_sizeis %ld clent off\n",res,recv_size); if ( close(i) == -1 ) { perror("close failed"); exit(EXIT_FAILURE); } FD_CLR(i, &readfds_bak); } printf("recved from new_sock=%d : %s(%ld length string)\n", i, buffer, recv_size); parse_socket_buf(buffer); //立即将收到的内容写回去,并关闭连接 // if ( send(i, buffer, recv_size, 0) == -1 ) // { // perror("send failed"); // close(i); // FD_CLR(i, &readfds_bak); // exit(EXIT_FAILURE); // } /* printf("send to new_sock=%d done\n", i); if ( close(i) == -1 ) { perror("close failed"); exit(EXIT_FAILURE); } printf("close new_sock=%d done\n", i); //将当前的socket从select的侦听中移除 FD_CLR(i, &readfds_bak); */ } } } return 0; } |
I really like it when folks come together and share ideas.
Great website, keep it up!