重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
系统提供select函数来实现多路复用输入/输出模型。select系统调用是用来让我们的程序监视多个文件句柄的状态变化的。
天心网站制作公司哪家好,找创新互联!从网页设计、网站建设、微信开发、APP开发、响应式网站设计等网站项目制作,到程序开发,运营维护。创新互联从2013年开始到现在10年的时间,我们拥有了丰富的建站经验和运维经验,来保证我们的工作的顺利进行。专注于网站建设就选创新互联。
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均被测试。
select函数修改三个描述符集,参数是值-结果参数,调用时,是关心描述符值。返回时,指示那些描述符已就绪。
描述符中任何与未就绪描述符对应的位返回时均被清为0,因此每次重调时,再次把所有描述符集中所关心的位置1.
返回值:大于0:所有描述符中已就绪的总位数。
等于0:超时
-1:出错
就绪条件:
//监视输入输出
#include#include #include #include #include #include int main() { int read_fd=0; int write_fd=1; fd_set reads; fd_set writes; FD_ZERO(&reads); FD_ZERO(&writes); struct timeval _timeout={5,0}; int ret=-1; int max_fd=write_fd; char buf[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://error perror("select"); break; case 0://time out printf("time is out...\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"); return 1; } printf("echo: %s",buf); } if(FD_ISSET(write_fd,&writes)) { strcpy(buf,"hello world"); printf("show: %s\n",buf); } } break; } } return 0;
运行截图:
实现TCP通信,处理任意个客户的单进程,而不是为每一个客户派生一个子进程。
创建监听套接字并初始化:调用socket,bind,listen,唯一描述符是监听描述符初始化数据结构。
阻塞于select:select等待某个事件发生或新客户连接的建立或是数据,FIN或RST的到达。
accept新连接
如果监听套接字变为可读,那么已建立一个新的连接,我们调用accept并更新相应数据结构。使用fds数组中第一个未用项记录这个已连接描述符。
检查现有连接
//server #include#include #include #include #include #include #include #include #include #include #define _BACKLOG_ 5 #define _SIZE_ 64 void Usage(const char* proc) { printf("%s [ip][port]\n",proc); } int Start(const char* _ip,int _port) { if(_ip==NULL) return -1; int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); return 1; } struct sockaddr_in local; local.sin_family=AF_INET; local.sin_addr.s_addr=inet_addr(_ip); local.sin_port=htons(_port); if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind"); return 2; } if(listen(sock,_BACKLOG_)<0) { perror("listen"); return 3; } return sock; } int main(int argc,char* argv[]) { if(argc!=3) { Usage(argv[0]); return 1; } char* ip=argv[1]; int port=atoi(argv[2]); int listen_sock=Start(ip,port); //allsets存储旧的描述符集 fd_set reads,allsets; int fds[_SIZE_]={0}; int max_fd=listen_sock; FD_ZERO(&reads); FD_ZERO(&allsets); FD_SET(listen_sock,&allsets); fds[0]=listen_sock; int i=0; for(i=1;i<_SIZE_;++i)//init fds { fds[i]=-1; } struct timeval _timeout={5,0}; struct sockaddr_in remote; socklen_t size=sizeof(remote); char buf[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://error perror("select"); return 2; case 0://timeout printf("time is out...\n"); break; default://normal { //printf("have a one is comming");//test for(i=0;i<_SIZE_;++i) { if(fds[i]==listen_sock&&FD_ISSET(fds[i],&reads)) { int newsock=accept(listen_sock,(struct sockaddr*)&remote,&size); if(newsock<0) { perror("accept"); continue; } FD_SET(newsock,&allsets); int j; for(j=0;j<_SIZE_;++j) { if(fds[j]==-1) { fds[j]=newsock; break; } } if(j==_SIZE_)//full close(newsock); break; } else if(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]); } } return 0; } //client #include #include #include #include #include #include #include #include void Usage(const char* proc) { printf("%s [ip][port]",proc); } int main(int argc,char* argv[]) { if(argc!=3) { Usage(argv[0]); return 1; } int client_sock=socket(AF_INET,SOCK_STREAM,0); if(client_sock<0) { perror("socket"); return 1; } struct sockaddr_in client; client.sin_family=AF_INET; client.sin_port=htons(atoi(argv[2])); client.sin_addr.s_addr=inet_addr(argv[1]); char buf[1024]; ssize_t _s; if(connect(client_sock,(struct sockaddr*)&client,sizeof(client))<0) { perror("connection"); return 2; } while(1) { printf("please enter:\n"); _s=read(0,buf,sizeof(buf)-1); if(_s>0) buf[_s]='\0'; if(strncmp(buf,"quit",4)==0) { printf("client is quit\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); return 0; }
//回显的