基于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) 的高性能回显服务器客户端模型的主要内容,如果未能解决你的问题,请参考以下文章

Linux网络编程基于TCP流 I/O多路转接(poll) 的高性能http服务器

I/O多路转接select/poll/epoll

select函数与I/O多路转接

IO多路转接 ——— selectpollepoll

select函数与I/O多路转接

I/O多路转接之select和非阻塞IO