我们使用IO复用函数最大的好处在于它实现了对于多个IO端口的监听。
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
struct timeval *timeout:is an upper bound on the amount of time elapsedbefore select() returns. If both fields of the timevalstucture are zero, then select() returns immediately. (This is useful for polling.) If timeout is NULL (no timeout),select() can block indefinitely.
#include<sys/time.h>
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
void FD_CLR(int fd, fd_set *set); //关闭set中的fd位
int FD_ISSET(int fd, fd_set *set); //检查fd是否在set集合中
void FD_SET(int fd, fd_set *set); //将set中的fd位置1
void FD_ZERO(fd_set *set);//将set置全0
79 fd_set fdset;
80 while(1)
81 {
82 FD_ZERO(&fdset);
83
84 int maxfd=-1;
85 int i=0;
86
87 for(;i<MAX;i++)
88 {
89 if(fds[i]!=-1) // ???
90 {
91 FD_SET(fds[i],&fdset);
92 if(maxfd<fds[i])
93 {
94 maxfd=fds[i];
95 }
96 }
当然还没有结束。接着我们要设置超时时间,接下来就是调用select函数,查看当前有哪些描述符是就绪的,接着select返回就绪描述符个数或者告诉我们在超时时间内没有就绪事件发生,或者出错返回-1.
99 struct timeval tv={5,0};
100 int n=select(maxfd+1,&fdset,NULL,NULL,&tv);
101 if(n==-1)
102 {
103 perror("select error");
104 break;
105 }
106 else if(n==0)
107 {
108 printf("timeout\n");
109 continue;
110 }
114 for(;i<MAX;i++)
115 {
116 if(fds[i]==-1)
117 {
118 continue;
119 }
120 if(FD_ISSET(fds[i],&fdset))
121 {
122 if(fds[i]==sockfd)
123 {
124 struct sockaddr_in caddr;
125 int len=sizeof(caddr);
126 int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
127 if(c<0)
128 {
129 continue;
130 }
131
132 printf("accept :%d\n",c);
133 fds_add(fds,c);
134 }
else
136 {
137 char buffer[128]={0};
138 if((recv(fds[i],buffer,127,0))<=0)
139 {
140 close(fds[i]); //close file descriptor
141 fds_del(fds,fds[i]);
142 printf("CLient over\n");
143 }
144 printf("buffer %d =%s\n",fds[i],buffer);
145 send(fds[i],"ok",2,0);
146
Under Linux, select() may report a socket file descriptor as "ready for reading", while nevertheless a subsequent read blocks. This could for example happen when data has arrived but upon examination has wrong checksum and is discarded.There may be other circumstances in which a file descriptor is spuriously reported as ready. Thus it may be safer to use O_NONBLOCK on sockets that should not block.
On Linux, select() also modifies timeout if the call is interrupted by a signal handler (i.e., the EINTR error return). This is not permitted by POSIX.1-2001.
最后我们总补充一下描述符就绪的条件:
在网络编程中,出现下情况我们认为socket是可读的。
(1)socket内核接收缓冲区的字节数大于或者等于第低水平标记的SO_RCVLOWAT.此时我可以无阻塞的读取socket,并且读操作返回的字节数大于零(数据缓存区有数据)
(2)scoket上有未处理了的错误。此时可用getsockpt来读取和清除该错误
(3)socket通信的对方关闭连接。此时对于该套接字的读操作返回0
(4)监听套接字上有新的连接
发生下面情况,我们认为socket是可写的
(1)套接字(socket)内核发送缓冲区中的可用字节数大于或者等于其低水位标记SO_SNDLOWAT,此时我们可以无阻塞的写socket,并且写操作返回的字节数大于零(数据缓存区有数据)
(2)套接字的写操作被关闭。对写操作被关闭的socket执行写操作将触发一个SIGPIPE信号。
(3)套接字使用非阻塞connect连接成功或者失败(超时)之后。
(4)套接字上有未处理了的错误。此时可用getsockpt来读取和清除该错误