系统提供select函数来实现多路复用输入/输出模型。select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。

select:该函数允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它。

参数含义:

timeout:它告知内核等待所制定描述符中的任何一个就绪可在多长时间,其timeval结构用于指定这段时间的秒数和微妙数。

struct timeval

{

long tv_sec;//seconds

long tv_usec;//microseconds

}

有三种情况:

永远等待下去:仅在有一个描述符准备好I/O时才返回,为此,我们把该参数设为NULL.

等待固定时间:在有一个描述符准备好I/O时返回,但是不超过由该参数所指向的timeval结构中指定的秒数和微妙数。

根本不等待:检查描述符后立即返回,这称为轮询,为此该参数必须指向一个timeval结构,而且其中的定时器值必须为0;

前两种情形的等待通常会被进程在等待期间捕获的信号中断,并从信号处理函数返回。

注:有的Linux会在select函数返回时修改timeval结构,从移植性考虑,我们应假设timeval结构在函数返回时未定义,因而每次调用select前都得对它进行初始化。
中间的三个参数readset,writeset和exceptset制定我们要内核测试读,写,异常条件的描述符。

异常原因:

1.某个套接字的带外数据的到达

2.某个已置为分组模式的伪终端存在可从其主端读取的控制状态信息

select使用描述符集,通常是一个×××数组,其中每个整数中每一位对应一个描述符

四个宏:

maxfd:待测试描述符个数:待测试描述符加1,描述符1,2...到maxfd-1均被测试。

<sys/select.h>中FD_SETSIZE常值是数据类型fd_set中描述符总数,其值通常是1024.

select函数修改三个描述符集,参数是值-结果参数,调用时,是关心描述符值。返回时,指示那些描述符已就绪。

描述符中任何与未就绪描述符对应的位返回时均被清为0,因此每次重调时,再次把所有描述符集中所关心的位置1.

返回值:大于0:所有描述符中已就绪的总位数。

等于0:超时

-1:出错

就绪条件:

//监视输入输出

#include<stdio.h>#include<stdlib.h>#include<errno.h>#include<sys/time.h>#include<sys/select.h>#include<string.h>intmain(){intread_fd=0;intwrite_fd=1;fd_setreads;fd_setwrites;FD_ZERO(&reads);FD_ZERO(&writes);structtimeval_timeout={5,0};intret=-1;intmax_fd=write_fd;charbuf[1024];while(1){FD_SET(read_fd,&reads);FD_SET(write_fd,&writes);_timeout.tv_sec=5;_timeout.tv_usec=0;ret=select(max_fd,&reads,&writes,NULL,&_timeout);switch(ret){case-1://errorperror("select");break;case0://timeoutprintf("timeisout...\n");break;default://normal{ssize_t_s;if(FD_ISSET(read_fd,&reads)){_s=read(0,buf,sizeof(buf)-1);buf[_s]='\0';if(strncmp(buf,"quit",4)==0){printf("quit\n");return1;}printf("echo:%s",buf);}if(FD_ISSET(write_fd,&writes)){strcpy(buf,"helloworld");printf("show:%s\n",buf);}}break;}}return0;

运行截图:

实现TCP通信,处理任意个客户的单进程,而不是为每一个客户派生一个子进程。

创建监听套接字并初始化:调用socket,bind,listen,唯一描述符是监听描述符初始化数据结构。

阻塞于select:select等待某个事件发生或新客户连接的建立或是数据,FIN或RST的到达。

accept新连接

如果监听套接字变为可读,那么已建立一个新的连接,我们调用accept并更新相应数据结构。使用fds数组中第一个未用项记录这个已连接描述符。

检查现有连接

对于每个现有客户连接,我们要测试其描述符是否在select返回描述符集中,如果是就从该客户读取一行文本,并回显,输出。如果该客户关闭了连接,那么read将返回0,更新数据结构。

//server#include<stdio.h>#include<stdlib.h>#include<errno.h>#include<string.h>#include<sys/select.h>#include<arpa/inet.h>#include<netinet/in.h>#include<sys/time.h>#include<sys/types.h>#include<sys/socket.h>#define_BACKLOG_5#define_SIZE_64voidUsage(constchar*proc){printf("%s[ip][port]\n",proc);}intStart(constchar*_ip,int_port){if(_ip==NULL)return-1;intsock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){perror("socket");return1;}structsockaddr_inlocal;local.sin_family=AF_INET;local.sin_addr.s_addr=inet_addr(_ip);local.sin_port=htons(_port);if(bind(sock,(structsockaddr*)&local,sizeof(local))<0){perror("bind");return2;}if(listen(sock,_BACKLOG_)<0){perror("listen");return3;}returnsock;}intmain(intargc,char*argv[]){if(argc!=3){Usage(argv[0]);return1;}char*ip=argv[1];intport=atoi(argv[2]);intlisten_sock=Start(ip,port);//allsets存储旧的描述符集fd_setreads,allsets;intfds[_SIZE_]={0};intmax_fd=listen_sock;FD_ZERO(&reads);FD_ZERO(&allsets);FD_SET(listen_sock,&allsets);fds[0]=listen_sock;inti=0;for(i=1;i<_SIZE_;++i)//initfds{fds[i]=-1;}structtimeval_timeout={5,0};structsockaddr_inremote;socklen_tsize=sizeof(remote);charbuf[1024];ssize_t_s;while(1){reads=allsets;_timeout.tv_sec=5;_timeout.tv_usec=0;for(i=0;i<_SIZE_;++i){if(fds[i]>max_fd)max_fd=fds[i];}switch(select(max_fd+1,&reads,NULL,NULL,&_timeout)){case-1://errorperror("select");return2;case0://timeoutprintf("timeisout...\n");break;default://normal{//printf("haveaoneiscomming");//testfor(i=0;i<_SIZE_;++i){if(fds[i]==listen_sock&&FD_ISSET(fds[i],&reads)){intnewsock=accept(listen_sock,(structsockaddr*)&remote,&size);if(newsock<0){perror("accept");continue;}FD_SET(newsock,&allsets);intj;for(j=0;j<_SIZE_;++j){if(fds[j]==-1){fds[j]=newsock;break;}}if(j==_SIZE_)//fullclose(newsock);break;}elseif(fds[i]>0&&FD_ISSET(fds[i],&reads)){_s=read(fds[i],buf,sizeof(buf)-1);if(_s==0){fds[i]=-1;FD_CLR(fds[i],&allsets);close(fds[i]);break;}buf[_s]='\0';printf("client:%s",buf);write(fds[i],buf,_s);}else{}}}break;}}for(i=0;i<_SIZE_;++i){if(fds[i]>0){close(fds[i]);}}return0;}//client#include<stdio.h>#include<stdlib.h>#include<errno.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<netinet/in.h>#include<string.h>voidUsage(constchar*proc){printf("%s[ip][port]",proc);}intmain(intargc,char*argv[]){if(argc!=3){Usage(argv[0]);return1;}intclient_sock=socket(AF_INET,SOCK_STREAM,0);if(client_sock<0){perror("socket");return1;}structsockaddr_inclient;client.sin_family=AF_INET;client.sin_port=htons(atoi(argv[2]));client.sin_addr.s_addr=inet_addr(argv[1]);charbuf[1024];ssize_t_s;if(connect(client_sock,(structsockaddr*)&client,sizeof(client))<0){perror("connection");return2;}while(1){printf("pleaseenter:\n");_s=read(0,buf,sizeof(buf)-1);if(_s>0)buf[_s]='\0';if(strncmp(buf,"quit",4)==0){printf("clientisquit\n");break;}write(client_sock,buf,_s);_s=read(client_sock,buf,sizeof(buf)-1);if(_s>0){buf[_s]='\0';printf("server->client:%s",buf);}}close(client_sock);return0;}

//回显的