基于TCP协议 I/O多路转接(select) 的高性能回显服务器客户端模型
Posted qiuri2008
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于TCP协议 I/O多路转接(select) 的高性能回显服务器客户端模型相关的知识,希望对你有一定的参考价值。
服务端代码:
myselect.c
1 #include <stdio.h> 2 #include <netinet/in.h> 3 #include <arpa/inet.h> 4 #include <sys/socket.h> 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <string.h> 8 #include <stdlib.h> 9 #include <unistd.h> 10 11 12 #define ARRAY_SIZE 1024 13 14 15 /* 使用说明 */ 16 void usage(const char* proc) 17 { 18 printf("%s usage : [server_ip] [ server_port]\n", proc); 19 } 20 21 22 /* 初始化一个socket连接 */ 23 int init_sock(const char* _ip, int _port) 24 { 25 int sock = socket(AF_INET, SOCK_STREAM, 0); 26 if(sock < 0) 27 { 28 perror("socket"); 29 exit(2); 30 } 31 32 int flg = 1; 33 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flg, sizeof(flg)); 34 struct sockaddr_in local; 35 local.sin_family = AF_INET; 36 local.sin_port = htons(_port); 37 local.sin_addr.s_addr = inet_addr(_ip); 38 39 if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0) 40 { 41 perror("bind"); 42 exit(3); 43 } 44 45 if(listen(sock , 10) < 0) 46 { 47 perror("listen"); 48 exit(4); 49 } 50 51 return sock; 52 } 53 54 /* 等待事件发生并处理 */ 55 void run_select(int listen_sock) 56 { 57 58 int array_fds[ARRAY_SIZE]; /* 保存所有关心的描述符 */ 59 array_fds[0] = listen_sock; /* 将listen_sock 保存*/ 60 61 /* 初始化为 array_fds */ 62 int idx_init = 1; 63 for(idx_init = 1; idx_init < ARRAY_SIZE; ++idx_init) 64 array_fds[idx_init] = -1; 65 66 fd_set rfds; /* 关心的读事件描述符集 */ 67 fd_set wfds; /* 关心的写事件描述符集 */ 68 int max_fd = -1; 69 70 struct timeval _timeout = {1, 0}; 71 72 while(1) 73 { 74 max_fd = -1; 75 _timeout.tv_sec = 1; 76 FD_ZERO(&rfds); 77 FD_ZERO(&wfds); 78 79 /* 一. 将数组中存储的描述符,添加到关心的集合中 80 * 并找出其中最大的描述符, 81 */ 82 int idx_add = 1; 83 for(idx_add = 0; idx_add < ARRAY_SIZE; idx_add++) 84 { 85 if(array_fds[idx_add] > 0) 86 { 87 FD_SET(array_fds[idx_add], &rfds); 88 FD_SET(array_fds[idx_add], &wfds); 89 if(array_fds[idx_add] > max_fd) 90 max_fd = array_fds[idx_add]; 91 92 } 93 } 94 /* 二. 检测select的返回情况*/ 95 int select_ret = select(max_fd+1, &rfds, &wfds, NULL, &_timeout); 96 switch( select_ret ) 97 { 98 case 0: 99 printf("timeout\n"); 100 break; 101 case -1: 102 perror("select\n"); 103 break; 104 default: 105 { 106 /* 遍历数组,检测事件发生的描述符,并响应 */ 107 int idx_check = 0; 108 for(idx_check = 0; idx_check < ARRAY_SIZE; ++idx_check) 109 { 110 if(array_fds[idx_check] < 0) 111 continue; 112 113 if(idx_check == 0 && FD_ISSET(array_fds[idx_check], &rfds) ) 114 { /* listensock 有数据来,表示有新的连接请求 */ 115 struct sockaddr_in client; 116 socklen_t len = sizeof(client); 117 118 int new_sock = accept(listen_sock, (struct sockaddr*)&client, &len); 119 if(new_sock > 0) 120 { 121 printf("新的连接请求来自: ip = %s, port = %d \n",inet_ntoa(client.sin_addr), ntohs(client.sin_port)); 122 123 /* 在数组中找到未被占用的描述符位置,并保存新连接的描述符 */ 124 int idx_find = 0; 125 for(idx_find = 1; idx_find < ARRAY_SIZE; ++idx_find) 126 { 127 if(array_fds[idx_find] < 0) 128 { 129 array_fds[idx_find] = new_sock; 130 break; 131 } 132 } 133 if(idx_find == ARRAY_SIZE) 134 close(new_sock); 135 } 136 } // << end if idx_check == 0 && FD_ISSET -- rfds>> 137 else if( idx_check != 0 && FD_ISSET(array_fds[idx_check], &rfds) ) 138 { /* 其余描述符有数据来*/ 139 140 char buf[ 1024]; 141 memset(buf, ‘\0‘, sizeof(buf)); 142 ssize_t s = read(array_fds[idx_check], buf, sizeof(buf) - 1); 143 if(s > 0) 144 { 145 printf("client say : %s\n", buf); 146 if(FD_ISSET(array_fds[idx_check], &wfds)) 147 write(array_fds[idx_check], buf, sizeof(buf)-1); 148 } 149 else if(s == 0) 150 { 151 printf("client quit\n"); 152 close(array_fds[idx_check]); 153 array_fds[idx_check] = -1; 154 } 155 else 156 { 157 perror("read fail"); 158 close(array_fds[idx_check]); 159 array_fds[idx_check] = -1; 160 } 161 } // << end else if idx_check != 0 && FD_ISSET -- rfds >> 162 } // << end for idx_check = 0; idx_check<ARRAY_SIZE; ++idx_check >> 163 } // << end default >> 164 break; 165 } // << end switch select >> 166 } // << end while 1 >> 167 } 168 169 int main(int argc, char **argv) 170 { 171 if(3 != argc) 172 { 173 usage(argv[0]); 174 exit(1); 175 } 176 177 int sock = init_sock(argv[1], atoi(argv[2])); 178 179 printf("start run_select\n"); 180 run_select(sock); 181 182 return 0; 183 }
客户端代码:
为了练习dup 和 dup2 函数的使用,在客户端中,使用了这两个函数进行标准输出的重定向以及恢复,使用printf 函数向sockfd 中写数据,并提示用户输入。
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <string.h> 4 #include <fcntl.h> 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <netinet/in.h> 8 #include <sys/socket.h> 9 #include <arpa/inet.h> 10 #include <stdlib.h> 11 12 int main(int argc, char **argv) 13 { 14 if(argc != 3) 15 { 16 printf("Usage [server_ip] [ server_port]\n"); 17 exit(1); 18 } 19 20 int sock = socket(AF_INET, SOCK_STREAM, 0); 21 if(sock < 0) 22 { 23 perror("socket"); 24 exit(2); 25 } 26 27 struct sockaddr_in server; 28 server.sin_family = AF_INET; 29 server.sin_addr.s_addr = inet_addr(argv[1]); 30 server.sin_port = htons(atoi(argv[2])); 31 32 33 34 if(connect(sock,(struct sockaddr*)&server, sizeof(server))<0) 35 { 36 perror("connect"); 37 exit(3); 38 } 39 40 // 保存标准输出的描述符 41 int oldfd = dup(STDOUT_FILENO); 42 char buf[1024]; 43 44 while(1) 45 { 46 printf("please input #"); 47 fflush(stdout); 48 memset(buf, ‘\0‘, sizeof(buf)); 49 50 /* 重定向输出到sock */ 51 dup2(sock, STDOUT_FILENO); 52 ssize_t s = read(0, buf, sizeof(buf)-1); 53 if(s > 0) 54 { 55 /* 处理客户端只输入一个回车而程序挂起的bug */ 56 if( buf[0] == ‘\n‘ ) 57 { 58 dup2(oldfd, STDOUT_FILENO); 59 continue; 60 } 61 62 if(strncmp("quit", buf, 4) == 0) 63 break; 64 65 buf[s - 1] = 0; 66 67 /* 用printf 向sock写 */ 68 printf("%s", buf); 69 fflush(stdout); 70 71 /* 恢复 标准输出 */ 72 dup2(oldfd, STDOUT_FILENO); 73 74 /* 从sock读服务端回显的数据, */ 75 ssize_t _s = read(sock, buf, sizeof(buf) - 1); 76 if(_s >0) 77 { 78 buf[_s] = 0; 79 printf("server echo # %s\n", buf); 80 } 81 else if (s <= 0) 82 { 83 continue; 84 } 85 } 86 } 87 88 close(sock); 89 close(oldfd); 90 91 return 0; 92 }
以上是关于基于TCP协议 I/O多路转接(select) 的高性能回显服务器客户端模型的主要内容,如果未能解决你的问题,请参考以下文章