咨询型网站,wordpress后台登陆地址修改,东莞的公司,省住房和城乡建设厅官方网站转载#xff1a;http://blog.csdn.net/u011573853/article/details/52105365 一#xff0c;什么是I/O多路复用 所谓的I/O多路复用在英文中其实叫 I/O multiplexing. 就是单个线程#xff0c;通过记录跟踪每个I/O流(sock)的状态#xff0c;来同时管理多个I/O流 。) I/O mu…转载http://blog.csdn.net/u011573853/article/details/52105365 一什么是I/O多路复用 所谓的I/O多路复用在英文中其实叫 I/O multiplexing. 就是单个线程通过记录跟踪每个I/O流(sock)的状态来同时管理多个I/O流 。) I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O流. 发明它的原因是尽量多的提高服务器的吞吐量 如图 二多路复用的方式很多今天说一下select原理 select函数会等待直到描述符句柄中有可用资源可读、可写、异常时返回返回值是可用资源可读/可写/异常等描述符的个数(0),0代表超时-1代表错误。具体到内核大致是当应用程序调用select() 函数, 内核就会相应调用 poll_wait() 把当前进程添加到相应设备的等待队列上然后将该应用程序进程设置为睡眠状态。直到该设备上的数据可以获取然后调用wake_up()唤醒该应用程序进程。select每次轮训都会遍历所有描述符句柄。 使用select模型的路径图如下 三函数 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); nfds: 监控的文件描述符集里最大文件描述符加1,因为此参数会告诉内核检测前多少个文件描述符的状态 readfds:监控有读数据到达文件描述符集合,传入传出参数 writefds:监控写数据到达文件描述符集合,传入传出参数 exceptfds:监控异常发生达文件描述符集合,如带外数据到达异常,传入传出参数 timeout:定时阻塞监控时间,3种情况 1.NULL,永远等下去 2.设置timeval,等待固定时间 3.设置timeval里时间均为0,检查描述字后立即返回,轮询 struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ }; void FD_CLR(int fd, fd_set *set); 把文件描述符集合里fd清0 int FD_ISSET(int fd, fd_set *set); 测试文件描述符集合里fd是否置1 void FD_SET(int fd, fd_set *set); 把文件描述符集合里fd位置1 void FD_ZERO(fd_set *set); 把文件描述符集合里所有位清0 select函数执行结果执行成功则返回文件描述词状态已改变的个数如果返回0代表在描述词状态改变前已超过timeout时间没有返回当有错误发生时则返回-1错误原因存于errno此时参数readfdswritefdsexceptfds和timeout的值变成不可预测。错误 值可能为 EBADF 文件描述词为无效的或该文件已关闭 EINTR 此调用被信号所中断 EINVAL 参数n 为负值。 ENOMEM 核心内存不足 要想理解好select模型就要理解好fd_set,为说明方便取fd_set长度为1字节fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。 1执行fd_set set;FD_ZERO(set);则set用位表示是0000,0000。 2若fd5,执行FD_SET(fd,set);后set变为0001,0000(第5位置为1) 3若再加入fd2fd1,则set变为0001,0011 4执行select(6,set,0,0,0)阻塞等待 5若fd1,fd2上都发生可读事件则select返回此时set变为0000,0011。注意没有事件发生的fd5被清空。 基于上面的讨论可以轻松得出select模型的特点 1)可监控的文件描述符个数取决与sizeof(fd_set)的值。我这边服务器上sizeof(fd_set)128每bit表示一个文件描述符则我服务器上支持的最大文件描述符是128*81024。 2将fd加入select监控集的同时还要再使用一个数据结构array保存放到select监控集中的fd一是用于再select返回后array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空则每次开始 select前都要重新从array取得fd逐一加入FD_ZERO最先扫描array的同时取得fd最大值maxfd用于select的第一个参数。 3可见select模型必须在select前循环array加fd取maxfdselect返回后循环arrayFD_ISSET判断是否有时间发生。 四优缺点 1简单可以在多种系统上使用 2select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024,单纯改变进程打开 的文件描述符个数并不能改变select监听文件个数 3解决1024以下客户端时使用select是很合适的,但如果链接客户端过多,select采用的是轮询模型,会大大降低服务器响应效率,不应在select上投入更多精力 五 案例 server #includestdio.h
#includeunistd.h
#includestdlib.h
#includestring.h
#includenetinet/in.h
#includearpa/inet.h
#includectype.h#define MAXLINE 80
#define SERV_PORT 8000int main(int atgc,char*argv[])
{int i,maxi,maxfd,listenfd,connfd,sockfd;int nready,client[FD_SETSIZE];ssize_t n;fd_set rset,allset;char buf[MAXLINE];char str[INET_ADDRSTRLEN];socklen_t cliaddr_len;struct sockaddr_in cliaddr,servaddr;listenfd socket(AF_INET,SOCK_STREAM,0);bzero(servaddr,sizeof(servaddr));servaddr.sin_family AF_INET;servaddr.sin_addr.s_addr htonl(INADDR_ANY);servaddr.sin_port htons(SERV_PORT);bind(listenfd,(struct sockaddr*)servaddr,sizeof(servaddr));listen(listenfd,20);maxfd listenfd;maxi -1;for(i 0;iFD_SETSIZE;i){client[i] -1;}FD_ZERO(allset);FD_SET(listenfd,allset);for(;;){rset allset;nready select(maxfd1,rset,NULL,NULL,NULL);if(nready 0){printf(select error \n);exit(1);}if(FD_ISSET(listenfd,rset)){cliaddr_len sizeof(cliaddr);connfd accept(listenfd,(struct sockaddr*)cliaddr,cliaddr_len);printf(received from %s at PORT %d \n,inet_ntop(AF_INET,cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));for(i0;iFD_SETSIZE;i)if(client[i]0){client[i]connfd;break;}if(iFD_SETSIZE){printf(too many clients\n);exit(1);}FD_SET(connfd,allset);if(connfdmaxfd)maxfd connfd;if(imaxi)maxi i;if(--nready0)continue;}for(i0;imaxi;i){if((sockfd client[i])0)continue;if(FD_ISSET(sockfd,rset)){if((nread(sockfd,buf,MAXLINE))0){close(sockfd);FD_CLR(sockfd,allset);client[i]-1;}else{int j;for(j0;jn;j)buf[j]toupper(buf[j]);write(sockfd,buf,n);}if(--nready 0)break;}}}close(listenfd);return 0;
}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990 client #includestdio.h
#includestdlib.h
#includeunistd.h
#includearpa/inet.h
#includenetinet/in.h
#includestring.h
#define MAXLINE 80
#define SERV_PORT 8000int main(int argc,char *argv[])
{struct sockaddr_in servaddr;char buf[MAXLINE];int sockfd,n;sockfd socket(AF_INET,SOCK_STREAM,0);bzero(servaddr,sizeof(servaddr));servaddr.sin_family AF_INET;inet_pton(AF_INET,192.168.1.103,servaddr.sin_addr);servaddr.sin_port htons(SERV_PORT);connect(sockfd,(struct sockaddr*)servaddr,sizeof(servaddr));while(fgets(buf,MAXLINE,stdin)!NULL){write(sockfd,buf,strlen(buf));n read(sockfd,buf,MAXLINE);if (n0){printf(the other ha closed\n);break;}elsewrite(STDOUT_FILENO,buf,n);}close(sockfd);return 0;
}12345678910111213141516171819202122232425262728293031323334 顶