linux下非阻塞网络编程-select

来源:互联网 发布:tv是哪个国家的域名 编辑:程序博客网 时间:2024/04/29 17:10
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 1024int 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]);elseserverport=5555;if(atoi(argv[2])>0)lisnum=atoi(argv[2]);elselisnum=10;
//创建socketif((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]);elseserveraddr.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 keyif(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));elseprintf("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端:
 
#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 1024int 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));}elseprintf("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;}elseprintf("message:%s send successfully,total:%d\n",buf,len);}}}close(sockfd);return 0;}
第一次在csdn上写这种带代码的文章,不好之处请见谅。望指教。