I/O多路复用——select
select
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
有三种类型的描述符类型:readset、writeset、exceptset,分别对应读、写、异常条件的描述符集合。fd_set 使用数组实现,数组大小使用 FD_SETSIZE 定义。
timeout 为超时参数,调用 select 会一直阻塞直到有描述符的事件到达或者等待的时间超过 timeout。
成功调用返回结果大于 0,出错返回结果为 -1,超时返回结果为 0。
select使用实例
服务端
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 #include <netinet/in.h>
4 #include <arpa/inet.h>
5 #include <unistd.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <strings.h>
9 #include <sys/wait.h>
10 #include <string.h>
11 #include <errno.h>
12 #define DEFAULT_PORT 6666
13 int main( int argc, char ** argv){
14 int serverfd,acceptfd; /* 监听socket: serverfd,数据传输socket: acceptfd */
15 struct sockaddr_in my_addr; /* 本机地址信息 */
16 struct sockaddr_in their_addr; /* 客户地址信息 */
17 unsigned int sin_size, myport=6666, lisnum=10;
18 if ((serverfd = socket(AF_INET , SOCK_STREAM, 0)) == -1) {
19 perror("socket" );
20 return -1;
21 }
22 printf("socket ok \n");
23 my_addr.sin_family=AF_INET;
24 my_addr.sin_port=htons(DEFAULT_PORT);
25 my_addr.sin_addr.s_addr = INADDR_ANY;
26 bzero(&(my_addr.sin_zero), 0);
27 if (bind(serverfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr )) == -1) {
28 perror("bind" );
29 return -2;
30 }
31 printf("bind ok \n");
32 if (listen(serverfd, lisnum) == -1) {
33 perror("listen" );
34 return -3;
35 }
36 printf("listen ok \n");
37
38 fd_set client_fdset; /*监控文件描述符集合*/
39 int maxsock; /*监控文件描述符中最大的文件号*/
40 struct timeval tv; /*超时返回时间*/
41 int client_sockfd[5]; /*存放活动的sockfd*/
42 bzero((void*)client_sockfd,sizeof(client_sockfd));
43 int conn_amount = 0; /*用来记录描述符数量*/
44 maxsock = serverfd;
45 char buffer[1024];
46 int ret=0;
47 while(1){
48 /*初始化文件描述符号到集合*/
49 FD_ZERO(&client_fdset);
50 /*加入服务器描述符*/
51 FD_SET(serverfd,&client_fdset);
52 /*设置超时时间*/
53 tv.tv_sec = 30; /*30秒*/
54 tv.tv_usec = 0;
55 /*把活动的句柄加入到文件描述符中*/
56 for(int i = 0; i < 5; ++i){
57 /*程序中Listen中参数设为5,故i必须小于5*/
58 if(client_sockfd[i] != 0){
59 FD_SET(client_sockfd[i], &client_fdset);
60 }
61 }
62 /*printf("put sockfd in fdset!\n");*/
63 /*select函数*/
64 ret = select(maxsock+1, &client_fdset, NULL, NULL, &tv);
65 if(ret < 0){
66 perror("select error!\n");
67 break;
68 }
69 else if(ret == 0){
70 printf("timeout!\n");
71 continue;
72 }
73 /*轮询各个文件描述符*/
74 for(int i = 0; i < conn_amount; ++i){
75 /*FD_ISSET检查client_sockfd是否可读写,>0可读写*/
76 if(FD_ISSET(client_sockfd[i], &client_fdset)){
77 printf("start recv from client[%d]:\n",i);
78 ret = recv(client_sockfd[i], buffer, 1024, 0);
79 if(ret <= 0){
80 printf("client[%d] close\n", i);
81 close(client_sockfd[i]);
82 FD_CLR(client_sockfd[i], &client_fdset);
83 client_sockfd[i] = 0;
84 }
85 else{
86 printf("recv from client[%d] :%s\n", i, buffer);
87 }
88 }
89 }
90 /*检查是否有新的连接,如果收,接收连接,加入到client_sockfd中*/
91 if(FD_ISSET(serverfd, &client_fdset)){
92 /*接受连接*/
93 struct sockaddr_in client_addr;
94 size_t size = sizeof(struct sockaddr_in);
95 int sock_client = accept(serverfd, (struct sockaddr*)(&client_addr), (unsigned int*)(&size));
96 if(sock_client < 0){
97 perror("accept error!\n");
98 continue;
99 }
100 /*把连接加入到文件描述符集合中*/
101 if(conn_amount < 5){
102 client_sockfd[conn_amount++] = sock_client;
103 bzero(buffer,1024);
104 strcpy(buffer, "this is server! welcome!\n");
105 send(sock_client, buffer, 1024, 0);
106 printf("new connection client[%d] %s:%d\n", conn_amount, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
107 bzero(buffer,sizeof(buffer));
108 ret = recv(sock_client, buffer, 1024, 0);
109 if(ret < 0){
110 perror("recv error!\n");
111 close(serverfd);
112 return -1;
113 }
114 printf("recv : %s\n",buffer);
115 if(sock_client > maxsock){
116 maxsock = sock_client;
117 }
118 else{
119 printf("max connections!!!quit!!\n");
120 break;
121 }
122 }
123 }
124 }
125 for(int i = 0; i < 5; ++i){
126 if(client_sockfd[i] != 0){
127 close(client_sockfd[i]);
128 }
129 }
130 close(serverfd);
131 return 0;
132 }
客户端
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <netinet/in.h>
8 #include <arpa/inet.h>
9 #include <errno.h>
10 #define DEFAULT_PORT 6666
11 int main( int argc, char * argv[]){
12 int connfd = 0;
13 int cLen = 0;
14 struct sockaddr_in client;
15 if(argc < 2){
16 printf(" Uasge: clientent [server IP address]\n");
17 return -1;
18 }
19 client.sin_family = AF_INET;
20 client.sin_port = htons(DEFAULT_PORT);
21 client.sin_addr.s_addr = inet_addr(argv[1]);
22 connfd = socket(AF_INET, SOCK_STREAM, 0);
23 if(connfd < 0){
24 perror("socket" );
25 return -1;
26 }
27 if(connect(connfd, (struct sockaddr*)&client, sizeof(client)) < 0){
28 perror("connect" );
29 return -1;
30 }
31 char buffer[1024];
32 bzero(buffer,sizeof(buffer));
33 recv(connfd, buffer, 1024, 0);
34 printf("recv : %s\n", buffer);
35 bzero(buffer,sizeof(buffer));
36 strcpy(buffer,"this is client!\n");
37 send(connfd, buffer, 1024, 0);
38 while(1){
39 bzero(buffer,sizeof(buffer));
40 scanf("%s",buffer);
41 int p = strlen(buffer);
42 buffer[p] = '\0';
43 send(connfd, buffer, 1024, 0);
44 printf("i have send buffer\n");
45 }
46 close(connfd);
47 return 0;
48 }