系统提供select函数来实现多路复用输入输出模型。select系统调用是用来让我们的程序监视多个文件的状态发生变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。关于文件句柄,其实就是一个整数:
0:代表标准输入(对应的FILE*结构是stdin)
1:是标准输出(对应的FILE*结构是stdout)
2:是标准错误(对应的FILE*结构是stderr)
函数返回值:
=0:表示等待超时
=-1:表示等待出错
>0:表示文件描述符中就绪文件描述符的个数
1、server服务器端:
1 #include<stdio.h>2 #include<stdlib.h>
3 #include<sys/types.h>
4 #include<sys/socket.h>
5 #include<netinet/in.h>
6 #include<arpa/inet.h>
7 #include<string.h>
8 #include<fcntl.h>
9 #include<sys/select.h>
10 #define SIZE sizeof(fd_set)*8
11
12 int fds[SIZE];
13
14 int startup(const char *ip,int port)
15 {
16 int sock=socket(AF_INET,SOCK_STREAM,0);
17 if(sock<0)
18 {
19 ┊ perror("socket");
20 ┊ exit(2);
21 }
22 struct sockaddr_in local;
23 local.sin_family=AF_INET;
24 local.sin_port=htons(port);
25 local.sin_addr.s_addr=inet_addr(ip);
26
27 if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
28 {
29 ┊ perror("bind");
30 ┊ exit(3);
31 }
32 if(listen(sock,10)<0)
33 {
34 ┊ perror("listen");
35 ┊ exit(4);
36 }
37 return sock;
38 }
39 static void usage(const char *proc)
40 {
41 printf("Usage:%s[local_ip][local_port]\n",proc);
42 }
43 int main(int argc,char* argv[])
44 {
45 if(argc!=3)
46 {
47 usage(argv[0]);
48 return 1;
49 }
50 int listen_sock=startup(argv[1],atoi(argv[2]));
51 int nums=sizeof(fds)/sizeof(fds[0]);
52 int i=0;
53 for(;i<nums;i++)
54 {
55 ┊ fds[i]=-1;
56 }
57 fds[0]=listen_sock;
58 while(1)
59 {
60 ┊ int max_fd=-1;
61 ┊ fd_set rfds;
62 ┊ FD_ZERO(&rfds);
63 ┊ for(i=0;i<nums;i++)
64 ┊ {
65 ┊ ┊ if(fds[i]>0)
66 ┊ ┊ {
67 ┊ ┊ ┊ FD_SET(fds[i],&rfds);
68 ┊ ┊ ┊ if(max_fd<fds[i])
69 ┊ ┊ ┊ {
70 ┊ ┊ ┊ ┊ max_fd=fds[i];
71 ┊ ┊ ┊ }
72 ┊ ┊ }
73 ┊ }
74 ┊ struct timeval timeout={1,0};
75 ┊ switch(select(max_fd+1,&rfds,NULL,NULL,&timeout))
76 ┊ {
77 ┊ ┊ case 0:
78 ┊ ┊ printf("timeout...\n");
79 ┊ ┊ break;
80 ┊ ┊ case -1:
81 ┊ ┊ perror("select");
82 ┊ ┊ default:
83 ┊ ┊ {
84 ┊ ┊ ┊ for(i=0;i<nums;i++)
85 ┊ ┊ ┊ {
86 ┊ ┊ ┊ ┊ if(fds[i]==-1)
87 ┊ ┊ ┊ ┊ {
88 ┊ ┊ ┊ ┊ ┊ continue;
89 ┊ ┊ ┊ ┊ }
90 ┊ ┊ ┊ ┊ if(i==0&&FD_ISSET(listen_sock,&rfds))
91 ┊ ┊ ┊ ┊ {
92 ┊ ┊ ┊ ┊ ┊ struct sockaddr_in client;
93 ┊ ┊ ┊ ┊ ┊ socklen_t len= sizeof(client);
94 ┊ ┊ ┊ ┊ ┊ int new_sock=accept(listen_sock,(struct sockaddr*)&client,&len);
95 ┊ ┊ ┊ ┊ ┊ if(new_sock<0)
96 ┊ ┊ ┊ ┊ ┊ {
97 ┊ ┊ ┊ ┊ ┊ ┊ perror("accept");
98 ┊ ┊ ┊ ┊ ┊ ┊ continue;
99 ┊ ┊ ┊ ┊ ┊ }
100 ┊ ┊ ┊ ┊ ┊ printf("get a new client:[%s:%d\n]", inet_ntoa(client.sin_addr),ntohs(client.sin_port));
101 ┊ ┊ ┊ ┊ ┊ int j=1;
102 ┊ ┊ ┊ ┊ ┊ for(;j<nums;j++)
103 ┊ ┊ ┊ ┊ ┊ {
104 ┊ ┊ ┊ ┊ ┊ ┊ if(fds[j]==-1)
105 ┊ ┊ ┊ ┊ ┊ ┊ {
106 ┊ ┊ ┊ ┊ ┊ ┊ ┊ break;
107 ┊ ┊ ┊ ┊ ┊ ┊ }
108 ┊ ┊ ┊ ┊ ┊ }
109 ┊ ┊ ┊ ┊ ┊ if(j==nums)
110 ┊ ┊ ┊ ┊ ┊ {
111 ┊ ┊ ┊ ┊ ┊ ┊ printf("client is full\n");
112 ┊ ┊ ┊ ┊ ┊ ┊ close(new_sock);
113 ┊ ┊ ┊ ┊ ┊ }
114 ┊ ┊ ┊ ┊ ┊ else
115 ┊ ┊ ┊ ┊ ┊ {
116 ┊ ┊ ┊ ┊ ┊ ┊ fds[i]=new_sock;
117 ┊ ┊ ┊ ┊ ┊ }
118 ┊ ┊ ┊ ┊ }
119 ┊ ┊ ┊ ┊ else if(i!=0&&FD_ISSET(fds[i],&rfds))
120 ┊ ┊ ┊ ┊ {
121 ┊ ┊ ┊ ┊ ┊ char buf[1024];
122 ┊ ┊ ┊ ┊ ┊ ssize_t s=read(fds[i],buf,sizeof(buf)-1);
123 ┊ ┊ ┊ ┊ ┊ if(s>0)
124 ┊ ┊ ┊ ┊ ┊ {
125 ┊ ┊ ┊ ┊ ┊ ┊ buf[s]=0;
126 ┊ ┊ ┊ ┊ ┊ ┊ printf("client#%s\n",buf);
127 ┊ ┊ ┊ ┊ ┊ }
128 ┊ ┊ ┊ ┊ ┊ else
129 ┊ ┊ ┊ ┊ ┊ {
130 ┊ ┊ ┊ ┊ ┊ ┊ perror("read");
131 ┊ ┊ ┊ ┊ ┊ ┊ close(fds[i]);
132 ┊ ┊ ┊ ┊ ┊ ┊ fds[i]=-1;
133 ┊ ┊ ┊ ┊ ┊ }
134 ┊ ┊ ┊ ┊ }
135 ┊ ┊ ┊ ┊ else
136 {
137
138 }
139 ┊ ┊ ┊ }
140 ┊ ┊ }
141 ┊ ┊ break;
142 ┊ }
143 }
144 return 0;
145 }
运行结果:
2、client客户端:
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<netinet/in.h>
4 #include<arpa/inet.h>
5 #include<sys/socket.h>
6 #include<stdlib.h>
7 #include<string.h>
8 #include<strings.h>
9 #include<sys/stat.h>
10 #include<unistd.h>
11
12 static void Usage(char *proc)
13 {
14 printf("Usage:%s[ip][port]\n");
15 }
16 int main(int argc,char*argv[])
17 {
18 if(argc!=3)
19 {
20 Usage(argv[0]);
21 return 1;
22 }
23 int sock=socket(AF_INET,SOCK_STREAM,0);
24 struct sockaddr_in peer;
25 peer.sin_family=AF_INET;
26 peer.sin_port=htons(atoi(argv[2]));
27 peer.sin_addr.s_addr=inet_addr(argv[1]);
28 peer.sin_port=htons(atoi(argv[2]));
29 peer.sin_addr.s_addr=inet_addr(argv[1]);
30 if(connect(sock,(struct sockaddr*)&peer,sizeof(peer))<0)
31 {
32 ┊ perror("connect");
33 ┊ return 2;
34 }
35 char buf[1024];
36 //int sfd=dup(STDOUT_FILENO);
37 while(1)
38 {
39 ┊ printf("Please enter:");
40 ┊ fflush(stdout);
41 ┊ ssize_t s=read(0,buf,sizeof(buf)-1);
42 ┊ int sfd=dup(STDOUT_FILENO);
43 ┊ if(s>0)
44 ┊ {
45 ┊ ┊ buf[s-1]=0;
46 ┊ ┊ //write(sock,buf,strlen(buf));
47 ┊ ┊ //shuchuchongdingxiang
48 ┊ ┊ //close(1);
49 ┊ ┊ //int new_fd=dup(sock);
50 ┊ ┊ int new_fd=dup2(sock,1);
51 ┊ ┊ if(new_fd==-1)
52 ┊ ┊ {
53 ┊ ┊ ┊ perror("dup()");
54 ┊ ┊ ┊ return -1;
55 ┊ ┊ }
56 ┊ ┊ printf("%s",buf);
57 ┊ ┊ fflush(stdout);
58 ┊ ┊ //huifu stdout
59 ┊ ┊ dup2(sfd,STDOUT_FILENO);
60 ┊ ┊ ssize_t _s=read(sock,buf,sizeof(buf)-1);
61 ┊ ┊ if(_s>0)
62 ┊ ┊ {
63 ┊ ┊ ┊ buf[_s]=0;
64 ┊ ┊ ┊ printf("server#%s\n",buf);
65 ┊ ┊ }
66 ┊ }
67 }
68 close(sock);
69 return 0;
70 }
3、总结:select服务器的优缺点与多进程/多线程服务器进行对比:
使用select注意事项:
1>要将sock_fd加入到maxfd+1中,要不就无法检测到网络连接,会一直阻塞在select语句
2>通过存储每次连接的描述符,设置FD_SET函数,在遍历的去判断FD_ISSET处理。
3>我们可以看到select每次有数据到来时,需要遍历的去寻找所有可用的描述符,来判断其是否满足处理的条件。
4>select的通知机制,轮询的去查看是否在maxfd+1内有满足条件的描述符
与多进程/多线程服务器进行对比 它的优点在于:
1)不需要建立多个线程、进程就可以实现一对多的通信。
2)可以同时等待多个文件描述符,效率比起多进程多线程来说要高很多。
3)select()的可移植性更好,在某些Unix系统上不支持poll()
4)select() 对于超时值提供了更好的精度:微秒,而poll是毫秒
与多进程/多线程服务器进行对比 它的缺点在于:
1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大 ,循环次数有点多;
2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大 。
3)select支持的文件描述符数量太小了,默认是1024;