select系统调用是用来让我们的程序监视多个文件句柄(file descriptor)的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变。
文件句柄在linux下很多,linux下函数返回是一个文件句柄被创建的都是。其实文件句柄就是一个整数,比如socket的函数声明int socket(int domain, int type, int protocol);我们最熟悉也是用的最多的是0,1,2,。0是标准输入,1是标准输出,2是标准错误输出。
select就是监视一个或多个句柄的状态变化。select函数的原型如下
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
函数的最后一个参数是超时时间值,其类型为struct timeval*,我们程序中要声明一个struct timeval类型的结构体,并传给select函数。struct timeval结构如下:
struct timeval{
long tv_sec;
long tv_usec;
}
select 函数的第一个参数为最大句柄值,一般要是最大句柄值+1;
后面3个参数类型都为 fd_set*,就是说明我们的程序要声明三个fd_set*类型的变量,然后把变量的地址传给函数,这个3个变量都是一个句柄集,readfds是表示可以读的句柄集,writefds表示可以写的句柄集,exceptfds表示特殊情况。函数的返回值是句柄集中句柄的个数。
fd_set 是一个结构体,对于该结构体的操作由一系列的宏来进行操作。
FD_ZERO(fd_Set* set) 清楚一个文件描述符集
FD_SET(int fd,fd_set* set) 将文件描述符fd加入到文件描述符集set中
FD_CLR(int fd,fd_set* set) 将文件描述符fd从文件描述符集set中删除
FD_ISSET(int fd,fd_set* set) 判断文件描述符是否被置位
在进行select的编程的时候,我们只需要检测我们所关心的状态变化,比如我们需要输入的时候,就关心是否可写,在不可写的时候,我们可以干其他的事,当可以写的时候就通知我们去进行输入。
下面将以一个异步的网络聊天程序进行说明:
Server端:
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/wait.h> #include <unistd.h> #include <sys/time.h> #define MAXBUF 1024 int main(int argc,char* argv[]){ int sockfd,new_fd; socklen_t len=sizeof(struct sockaddr); struct sockaddr_in serveraddr,clientaddr; unsigned int serverport,lisnum; char buf[MAXBUF+1]; fd_set rfds; //文件描述符集 struct timeval tv; int retval,maxfd=-1; if(argv[1]>0) serverport=atoi(argv[1]); else serverport=5555; if(atoi(argv[2])>0) lisnum=atoi(argv[2]); else lisnum=10;
//创建socket if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){ perror("socket error"); exit(1); } printf("port=%d\n",serverport); printf("listen num=%d\n",lisnum); bzero(&serveraddr,sizeof(serveraddr)); serveraddr.sin_family=AF_INET; serveraddr.sin_port=htons(serverport); if(argv[3]) serveraddr.sin_addr.s_addr=inet_addr(argv[3]); else serveraddr.sin_addr.s_addr=INADDR_ANY; if((bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(struct sockaddr)))==-1){ perror("bind error"); exit(1); } if(listen(sockfd,lisnum)==-1){ perror("listen error"); exit(1); } while(1){ printf("\n----Wait new connection to chat-------\n"); len=sizeof(struct sockaddr); if((new_fd=accept(sockfd,(struct sockaddr*)&clientaddr,&len))==-1){ perror("accept error"); exit(1); }else{ printf("Server:get connection from %s,port %d,socket %d\n", inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port),new_fd); } printf("\n---ready for chat----------------\n"); while(1){ //清空文件描述符集 FD_ZERO(&rfds); //将标准输入添加到文件描述集中
FD_SET(0,&rfds); maxfd=0; FD_SET(new_fd,&rfds); //将socket添加到set中 if(new_fd>maxfd) maxfd=new_fd; tv.tv_sec=1; tv.tv_usec=0; retval=select(maxfd+1,&rfds,NULL,NULL,&tv); if(retval==-1){ printf("quit chat!select error!\n"); break; }else if(0==retval){ // printf("No message,No keydown,please wait.......\n"); continue; }else{ //user put key if(FD_ISSET(0,&rfds)){ //有输入 bzero(buf,sizeof(buf)); fgets(buf,sizeof(buf),stdin); if(strncmp(buf,"quit",4)==0){ printf("quit chat by self\n"); break; } len=send(new_fd,buf,strlen(buf)-1,0); if(len>0) printf("message:%ssend successfully,total size:%d\n",buf,len); else{ printf("message:%s send failure!,error number:%d,error message:%s\n",buf,errno,strerror(errno)); break; } } if(FD_ISSET(new_fd,&rfds)){ //有消息到来 bzero(buf,sizeof(buf)); len=recv(new_fd,buf,MAXBUF,0); if(len>0) printf("recv message %s successfully,total:%d\n",buf,len); else{ if(len<0) printf("recv message failure!error number:%d,error mesage:%s",errno,strerror(errno)); else printf("Connection is quit,chat end\n"); break; } } } } close(new_fd); printf("do you continue chat(no:quit):"); fflush(stdout); bzero(buf,sizeof(buf)); fgets(buf,sizeof(buf),stdin); if(strncmp(buf,"no",2)==0){ printf("chat end\n"); break; } } close(sockfd); return 0; }
client端:
第一次在csdn上写这种带代码的文章,不好之处请见谅。望指教。#include <stdio.h> #include <string.h> #include <errno.h> #include <sys/socket.h> #include <resolv.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #define MAXBUF 1024 int main(int argc,char* argv[]){ int sockfd,len=sizeof(struct sockaddr); struct sockaddr_in serveraddr; char buf[MAXBUF+1]; fd_set rfds; struct timeval tv; int retval,maxfd=-1; if(argc!=3){ printf("please input server addr!\n"); exit(0); } if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){ perror("socket error!"); exit(1); } bzero(&serveraddr,sizeof(serveraddr)); serveraddr.sin_family=AF_INET; serveraddr.sin_port=htons(atoi(argv[2])); if(inet_aton(argv[1],(struct in_addr*)&serveraddr.sin_addr.s_addr)==0){ perror(argv[1]); exit(1); } printf("%s %d\n",inet_ntoa(serveraddr.sin_addr),ntohs(serveraddr.sin_port)); if((connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr)))==-1){ perror("Connect error"); exit(1); } printf("\n read for chat-------------------------\n"); while(1){ FD_ZERO(&rfds); FD_SET(0,&rfds); maxfd=0; FD_SET(sockfd,&rfds); if(sockfd>maxfd) maxfd=sockfd; tv.tv_sec=1; tv.tv_usec=0; retval=select(maxfd+1,&rfds,NULL,NULL,&tv); if(retval==-1){ printf("quit,select error:%s\n",strerror(errno)); break; }else if(retval==0){ // printf("no message,.no keydown----------\n"); continue; }else{ if(FD_ISSET(sockfd,&rfds)){ bzero(buf,sizeof(buf)); len=recv(sockfd,buf,MAXBUF,0); if(len>0){ printf("recv message:%s total:%d\n",buf,len); }else{ if(len<0){ printf("recv message failure,errno:%d,error message:%s\n",errno,strerror(errno)); }else printf("quit,chat end!\n"); break; } } if(FD_ISSET(0,&rfds)){ bzero(buf,sizeof(buf)); fgets(buf,MAXBUF,stdin); if(strncmp(buf,"quit",4)==0){ printf("quit chat!\n"); break; } len=send(sockfd,buf,strlen(buf)-1,0); if(len<0){ printf("send message failure!errno:%d,error message:%s\n",errno,strerror(errno)); break; }else printf("message:%s send successfully,total:%d\n",buf,len); } } } close(sockfd); return 0; }
linux下非阻塞网络编程-select
最新推荐文章于 2022-10-11 21:37:32 发布