您的位置:首页 > 理论基础 > 计算机网络

I/O多路转接之select——基于TCP协议

2016-05-28 14:23 567 查看
关于select的基础知识
a. select是系统提供的多路复用输入输出模型。它是用来监视多个文件句柄的状态变化。
b. 程序会停在select等,直到被监视的文件句柄至少有一个发生了状态改变。
c. 文件句柄是整数。例如:我们最熟悉的句柄是0、1、2,0是标准输入,1是标准输出,2是标准错误输出。0、1、2对应的FILE *结构的表示是stdin、stdout、stderr。

2. 函数
(1)select()



a. 参数
nfds:需要监视的最大文件描述符值加1;
readfds:select监视的可读文件句柄集合。
writefds: select监视的可写文件句柄集合。
exceptfds:select监视的异常文件句柄集合。
timeout:是struct timeval结构用于描述一段时间长度。用来设置select()的等待时间,其结构定义如下:




如果参数timeout设为:
NULL:select将一直被阻塞,直到某个文件描述符上发生了事件。

0:select将以非阻塞方式等,即检测描述符集的状态后立即返回,并不等待外部事件的发生。

大于0的值:

如果在指定的时间段里没有事件发生,select将超时返回0。

当readfds或writefds中映象的文件可读或可写或超时,本次select()结束返回。程序员利用一组系统提供的宏在select()结束时便可判断哪一文件可读或可写。
(见/usr/sys/select.h,可精确至百万分之一秒!)★除第一个外,所有参数既是输入型参数又是输出型参数。

b.返回值成功则返回文件描述词状态已改变的数目;

有错误返回-1;

超过timeout时间,返回0。

(2)几行相关的宏解释如下:
void FD_CLR(int fd, fd_set *set);  //清除文件句柄fd与set的联系。
int  FD_ISSET(int fd, fd_set *set); //检查set联系的文件句柄fd是否可读写,当>0表示可读写。
void FD_SET(int fd, fd_set *set);  //建立文件句柄fd与set的联系。
void FD_ZERO(fd_set *set);       //清空set与所有文件句柄的联系。
(关于fd_set及相关宏的定义见/usr/include/sys/types.h

3.代码实现
//select_server.c
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<assert.h>
4 #include<sys/select.h>
5 #include<sys/types.h>
6 #include<sys/socket.h>
7 #include<arpa/inet.h>
8 #include<netinet/in.h>
9
10 #define _BACKLOG_ 5
11 //将待处理的文件句柄保存放到select监控集中的fd
12 int fds[64];
13
14 static void usage(const char* proc)
15 {
16     printf("usage:%s [ip] [port]\n",proc);
17 }
18 static int startup(char* ip,int port)
19 {
20     assert(ip);
21     //创建socket
22     int sock=socket(AF_INET,SOCK_STREAM,0);
23     if(sock<0)
24     {
25         perror("socket");
26         exit(1);
27     }
28
28     //设置本地server端网络地址信息
29     struct sockaddr_in local;
30     local.sin_family=AF_INET;
31     local.sin_port=htons(port);
32     local.sin_addr.s_addr=inet_addr(ip);
33     //绑定
34     if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
35     {
36         perror("bind");
37         exit(2);
38     }
39     //监听
40     if(listen(sock,_BACKLOG_)<0)
41     {
42         perror("listen");
43         exit(3);
44     }
45     return sock;
46 }
47 int main(int argc,char*argv[])
48 {
49     if(argc!=3)
50     {
51         usage(argv[0]);
52         exit(1);
53     }
54
55     char* ip=argv[1];
56     int port=atoi(argv[2]);
57     //获取socket
58     int listen_sock=startup(ip,port);
59
60     int done=0;
61     int new_sock=-1;
62     struct sockaddr_in client;//创建网络地址信息结构体用于保存对端信息
63     socklen_t len=sizeof(client);
64
65     int max_fd;//最大文件描述符
66     fd_set _reads;//可读文件句柄集合
67     fd_set _writes;//可写文件句柄集合
68
69     int i=0;
70     int fds_num=sizeof(fds)/sizeof(fds[0]);
71     for(;i<fds_num;++i)//初始化发fds
72     {
73         fds[i]=-1;
74     }
75     fds[0]=listen_sock;//将fds的第一个元素设为监听套接字
76     max_fd=fds[0];//最大文件描述符设为fds第一个元素
77
78     while(!done)
79     {
80         FD_ZERO(&_reads);//清空_reads与所有文件句柄的联系。
81         FD_ZERO(&_writes);//清空_writes与所有文件句柄的联系。
82         FD_SET(listen_sock,&_reads);建立文件句柄lisen_sock与_reads的联系
83         struct timeval _timeout={5,0};//设定超时时间
84         for(i=1;i<fds_num;++i)//循环fds(加fd,取maxfd)
85         {
86             if(fds[i]>0)
87             {
88                 FD_SET(fds[i],&_reads);
89                 if(fds[i]>max_fd)
90                 {
91                     max_fd=fds[i];
92                 }
93             }
94         }
95         switch(select(max_fd+1,&_reads,&_writes,NULL,NULL))
96         {
97             case 0:
98                 //超时
99                 printf("timeout\n");
100                 break;
101             case -1://出错
102                 perror("select");
103                 break;
104             default://至少有一个IO事件已经就绪
105                 {
106                     i=0;
107                     for(;i<fds_num;++i)
108                     {
109                         if(fds[i]==listen_sock&&FD_ISSET(fds[i],&_reads))
110                         {
111                             //listen socket is ready!
112                             new_sock=accept(listen_sock,(struct sockaddr*)&client,&len);
113                             if(new_sock<0)
114                             {
115                                 perror("accept");
116                                 continue;
117                             }
118                             printf("get a new connect...%d\n",new_sock);
119                             for(i=0;i<fds_num;++i)//将新的连接请求加入到fds
120                             {
121                                 if(fds[i]==-1)
122                                 {
123                                     fds[i]=new_sock;
124                                     break;
125                                 }
126                             }
127                             if(i==fds_num)//如果i等于fds数组的大小,关掉new_sock
128                             {
129                                 close(new_sock);
130                             }
131                         }
132                         //别的文件句柄
133                         else if(fds[i]>0 && FD_ISSET(fds[i],&_reads))
134                         {
135                             char buf[1024];
136                             ssize_t _s=read(fds[i],buf,sizeof(buf)-1);
137                             if(_s>0)
138                             {
139                                 //read sucess
140                                 buf[_s]='\0';
141                                 printf("client:%s\n",buf);
142                             }
143                             else if(_s==0)
144                             {
145                                 //client shutdown
146                                 printf("client shutdown...\n");
147                                 close(fds[i]);
148                                 fds[i]=-1;
149                             }
150                             else
151                             {
152                                 perror("read");
153                             }
154                         }
155                         //normal socket
156                         else
157                         {}
158                     }
159                 }
160                 break;
161         }
162     }
163     return 0;
164 }

//select_client.c
1 #include<stdio.h>
2 #include<string.h>
3 #include<sys/select.h>
4 int main()
5 {
6     int std_in=0;
7
8     int max_fd=std_in;
9     struct timeval _timeout={5,0};
10
11     fd_set _reads;
12
13     FD_ZERO(&_reads);
14     FD_SET(std_in,&_reads);
15
16     int done=0;
17     while(!done)
18     {
19         //set timeout
20         _timeout.tv_sec=5;
21         _timeout.tv_usec=0;
22
23         switch(select(max_fd+1,&_reads,NULL,NULL,&_timeout))
24         {
25             case -1:
26                 //error
27                 perror("select");
28                 break;
29             case 0:
30                 printf("select timeout...\n");
31                 break;
32             default:
33                 {
34                     if(FD_ISSET(std_in,&_reads))
35                     {
36                         //read event ready
37                         char buf[1024];
38                         memset(buf,'\0',sizeof(buf));
39                         ssize_t _s = read(std_in,buf,sizeof(buf)-1);
40                         if(_s>0)
41                         {
42                             buf[_s]='\0';
43                             if(strncmp(buf,"quit",4)==0)
44                             {
45                                 done=1;
46                                 continue;
47                             }
48                             printf("echo:%s\n",buf);
49                         }
50                     }
51                 }
52                 break;
53         }
54     }
55     return 0;
56 }

//makefile
1 .PHONY:all
2 all:select_server select_client
3 select_server:select_server.c
4     gcc -o $@ $^
5 select_client:select_client.c
6     gcc -o $@ $^
7 .PHONY:clean
8 clean:
9     rm -f select_server select_client

//start.sh
1 #!/bin/bash
2
3 service iptables stop
4 ./select_server 192.168.163.128 8080
输出结果:
修正版:
//select_client.c
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include<sys/select.h>
5 #include<sys/socket.h>
6 #include<netinet/in.h>
7 #include<unistd.h>
8
9 void usage(const char* proc)
10 {
11     printf("%s [ip] [port]\n",proc);
12     exit(1);
13 }
14 int main(int argc,char* argv[])
15 {
16     if(argc!=3)
17     {
18         usage(argv[0]);
19         exit(1);
20     }
21     int server_ip=inet_addr(argv[1]);
22     int server_port=atoi(argv[2]);
23
24     int client_sock=socket(AF_INET,SOCK_STREAM,0);
25     if(client_sock<0)
26     {
27         perror("socket");
28         exit(2);
29     }
30     struct sockaddr_in server;
31     server.sin_family=AF_INET;
32     server.sin_addr.s_addr=server_ip;
33     server.sin_port=htons(server_port);
34
35     if(connect(client_sock,(struct sockaddr*)&server,sizeof(server))<0)
36     {
37         perror("connect");
38         exit(3);
39     }
40     char buf[1024];
41     while(1)
42     {
43         memset(buf,'\0',sizeof(buf));
44         printf("Please Input: ");
45         fflush(stdout);
46         fgets(buf,sizeof(buf)-1,stdin);
47         if(send(client_sock,buf,sizeof(buf)-1,0)<0)
48         {
49             perror("send");
50             continue;
51         }
52         ssize_t _size=recv(client_sock,buf,sizeof(buf)-1,0);
53         if(_size>0)
54         {
55             buf[_size]='\0';
56             printf("server receive:%s\n",buf);
57         }
58     }
59     return 0;
60 }

//select_server.c
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<assert.h>
4 #include<sys/select.h>
5 #include<sys/types.h>
6 #include<sys/socket.h>
7 #include<arpa/inet.h>
8 #include<netinet/in.h>
9
10 #define _BACKLOG_ 5
11
12 int fds[64];
13
14 static void usage(const char* proc)
15 {
16     printf("usage:%s [ip] [port]\n",proc);
17 }
18 static int startup(char* ip,int port)
19 {
20     assert(ip);
21     //
22     int sock=socket(AF_INET,SOCK_STREAM,0);
23     if(sock<0)
24     {
25         perror("socket");
26         exit(1);
27     }
28     //
29     struct sockaddr_in local;
30     local.sin_family=AF_INET;
31     local.sin_port=htons(port);
32     local.sin_addr.s_addr=inet_addr(ip);
33     //
34     if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
35     {
36         perror("bind");
37         exit(2);
38     }
39     //
40     if(listen(sock,_BACKLOG_)<0)
41     {
42         perror("listen");
43         exit(3);
44     }
45     return sock;
46 }
47 int main(int argc,char*argv[])
48 {
49     if(argc!=3)
50     {
51         usage(argv[0]);
52         exit(1);
53     }
54
55     char* ip=argv[1];
56     int port=atoi(argv[2]);
57
58     int listen_sock=startup(ip,port);
59
60     int done=0;
61     int new_sock=-1;
62     struct sockaddr_in client;
63     socklen_t len=sizeof(client);
64
65     int max_fd;
66     fd_set _reads;
67     fd_set _writes;
68
69     int i=0;
70     int fds_num=sizeof(fds)/sizeof(fds[0]);
71     for(;i<fds_num;++i)
72     {
73         fds[i]=-1;
74     }
75     fds[0]=listen_sock;
76     max_fd=fds[0];
77
78     while(!done)
79     {
80         FD_ZERO(&_reads);
81         FD_ZERO(&_writes);
82         FD_SET(listen_sock,&_reads);
83         struct timeval _timeout={5,0};
84         for(i=1;i<fds_num;++i)
85         {
86             if(fds[i]>0)
87             {
88                 FD_SET(fds[i],&_reads);
89                 FD_SET(fds[i],&_writes);
90                 if(fds[i]>max_fd)
91                 {
92                     max_fd=fds[i];
93                 }
94             }
95         }
96         switch(select(max_fd+1,&_reads,&_writes,NULL,&_timeout))
97         {
98             case 0:
99                 //timeout
100                 printf("timeout...\n");
101                 break;
102             case -1:
103                 perror("select");
104                 break;
105             default:
106                 {
107                     i=0;
108                     for(;i<fds_num;++i)
109                     {
110                         if(fds[i]==listen_sock&&FD_ISSET(fds[i],&_reads))
111                         {
112                             //listen socket is ready!
113                             new_sock=accept(listen_sock,(struct sockaddr*)&client,&len);
114                             if(new_sock<0)
115                             {
116                                 perror("accept");
117                                 continue;
118                             }
119                             char* client_ip=inet_ntoa(client.sin_addr);
120                             int client_port=ntohs(client.sin_port);
121                             printf("get a new connect...[ip]:%s  [port]:%d\n",client_ip,client_port);
122                             for(i=0;i<fds_num;++i)
123                             {
124                                 if(fds[i]==-1)
125                                 {
126                                     fds[i]=new_sock;
127                                     break;
128                                 }
129                             }
130                             if(i==fds_num)
131                             {
132                                 close(new_sock);
133                             }
134                         }
135                         //listen socket
136                         else if(fds[i]>0 && FD_ISSET(fds[i],&_reads))
137                         {
138                             char buf[1024];
139                             ssize_t _s=read(fds[i],buf,sizeof(buf)-1);
140                             if(_s>0)
141                             {
142                                 //read sucess
143                                 buf[_s]='\0';
144                                 printf("client:%s",buf);
145                                 if(FD_ISSET(fds[i],&_writes));
146                                 {
147                                     _s=write(fds[i],buf,sizeof(buf)-1);
148                                     if(_s>0)
149                                     {
150                                         //write sucess
151                                      //   buf[_s]='\0';
152                                     //    printf("server:%s\n",buf);
153                                     }
154                                     else if(_s<0)
155                                     {
156                                         perror("write");
157                                         exit(1);
158                                     }
159                                     else
160                                     {
161                                         printf("can not write back...\n");
162                                     }
163                                 }
164                             }
165                             else if(_s==0)
166                             {
167                                 //client shutdown
168                                 printf("client shutdown...\n");
169                                 close(fds[i]);
170                                 fds[i]=-1;
171                             }
172                             else
173                             {
174                                 perror("read");
175                             }
176                         }
177                         //normal socket
178                         else
179                         {}
180                     }
181                 }
182                 break;
183         }
184     }
185     return 0;
186 }
输出结果:


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  TCP I/O select