select---基于TCP客户/服务端编程
我们先来说说最重要的函数select这个函数,它的原型如下:
intselect(intnfds,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,structtimeval*timeout);
select函数可以执行I/O多路转接。传给select的参数告诉内核:
1.我们所关心的描述符
2.对于每个描述符我们所关心的条件(是否想从一个给定的描述符读,写,还是异常条件)
3.愿意等待的时间(永远等待,等待一个给定的时间或者根本不等待)
从select返回时,内核告诉我们:
1.已准备好的描述符的总数量
2.对于读,写,异常这3个条件中的每一个,哪些描述符已经准备好
有了上面的返回信息,我们就可以调用相应的I/O函数(read或者write),并且确知该函数不会被阻塞。
我们再来说说select这个函数的返回值和相应的参数:
正常返回准备就绪的描述符数目,若超时,返回0;出错时返回-1.
参数:
我们先来说最后一个参数,它是一个指向结构体的指针,这个结构体如下:
structtimeval
{
long tv_sec;
long tv_usec;
}
这个参数指明了愿意等待的时间长度,单位为秒和微妙。当它的值为NULL,说明要永远等待。如果捕捉到一个信号则中断无限等待。当所指定的描述符的一个已经准备好或捕捉到一个信号则返回。当这个结构体的两个成员的值均不为0时,则是我们设定的要指定的秒数和微秒数。当指定的一个描述符之一已经准备好,或者当指定的时间已经超过时立即返回。在超时后还没有一个描述符准备好,返回0。
中间的 readfds,writefds,exceptfds这三个参数是指向描述符集的指针。这3个描述符集说明了我们关心的可读,可写或处于异常条件的描述符集合。每个描述符集存储在一个fd_set数据类型中。这个数据类型为每一个描述符保持一位。在这里我们简单的认为是一个很大的字节数组。
第一个参数nfds的意思是“最大文件描述符编号值加1”。因为描述符从0 开始,所以要在最大描述符编号上加1.意思是我们我们在所有描述符集当中找出最大的一个,然后加1就行了。这里我们也可以将第一个参数设置为FD_SETSIZE,它是一个常量值,它指定最大描述符数(一般是1024),一般而言,此值有点大。通过我们指定最大的描述符数,内核就只需在此范围内寻找打开的位,大大节省了时间。
我们再来说说fd_set这个数据类型:
这种数据类型的变量经常用到以下函数:
intFD_ISSET(int fd,fd_set *fdset);
void FD_CLR(int fd,fd_set *fdset);
void FD_SET(int fd,fd_set *fdset);
void FD_ZERO(fd_set* fdset);
这些接口可实现为宏或者函数。调用FD_ZERO将一个fd_set变量的所有位设置为0.调用FD_SET设置描述符集中的一位。调用FD_CLR可以清除一位。我们可以调用FD_ISSET测试描述符集中的一个指定位是否已经打开。我们要注意,在我们声明了一个描述符集之后,必须调用FD_ZERO函数将这个描述符集置为0,然后再在其中设置我们关心的各个描述符的位。
上面说了那么多,我们来用用这几个函数吧,在下面的程序中,我们用刚说的那几个函数写了一个简单的客户/服务器间通信模型,客户向服务端发送数据,服务端收到后再回显给客户端,我们来看看具体代码:
server端:
#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<assert.h>#include<unistd.h>#include<netinet/in.h>#include<arpa/inet.h>#include<sys/socket.h>#define_BACKLOG_5intfds[64];staticvoidusage(constchar*arg){printf("usage:%s[ip][poort]",arg);}staticintstartup(char*ip,intport){assert(ip);intsock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){perror("socket");exit(1);}structsockaddr_inlocal;local.sin_family=AF_INET;local.sin_port=htons(port);local.sin_addr.s_addr=inet_addr(ip);if(bind(sock,(structsockaddr*)&local,sizeof(local))<0){perror("bind");exit(2);}if(listen(sock,_BACKLOG_)<0){perror("listen");exit(3);}returnsock;}intmain(intargc,char*argv[]){if(argc!=3){usage(argv[0]);exit(1);}intport=atoi(argv[2]);char*ip=argv[1];intlisten_sock=startup(ip,port);intdone=0;intnew_sock=-1;structsockaddr_inclient;socklen_tlen=sizeof(client);intmax_fd;fd_setreads;fd_setwrits;inti=0;intfds_num=sizeof(fds)/sizeof(fds[0]);for(;i<fds_num;++i){fds[i]=-1;}fds[0]=listen_sock;max_fd=fds[0];while(!done){FD_ZERO(&reads);FD_ZERO(&writs);FD_SET(listen_sock,&reads);structtimevaltimeout={5,0};for(i=1;i<fds_num;++i){if(fds[i]>0){FD_SET(fds[i],&reads);if(fds[i]>max_fd){max_fd=fds[i];}}}switch(select(max_fd+1,&reads,&writs,NULL,&timeout)){case0://超时printf("selecttimeout\n");break;case-1:perror("select");break;default:{i=0;for(;i<fds_num;++i){if(fds[i]==listen_sock&&FD_ISSET(fds[i],&reads)){//监听套接字就绪new_sock=accept(listen_sock,(structsockaddr*)&client,&len);if(new_sock<0){perror("accept");continue;}printf("getanewconnect...%d\n",new_sock);for(i=0;i<fds_num;++i){if(fds[i]==-1){fds[i]=new_sock;break;}}if(i==fds_num){close(new_sock);}}//listensockelseif(fds[i]>0&&FD_ISSET(fds[i],&reads)){charbuf[1024];ssize_t_s=read(fds[i],buf,sizeof(buf)-1);if(_s>0){buf[_s]='\0';printf("client:%s",buf);write(fds[i],buf,_s);}elseif(_s==0){printf("clientquit..\n");close(fds[i]);fds[i]=-1;}else{}}//nomalsocketelse{}}}break;}}return0;}
下面是client端:
#include<stdio.h>#include<errno.h>#include<stdlib.h>#include<string.h>#include<netinet/in.h>#include<arpa/inet.h>#include<sys/types.h>#include<sys/socket.h>voidusage(constchar*arg){printf("%s[remote_ip][remote_port\n",arg);}intmain(intargc,char*argv[]){if(argc!=3){usage(argv[0]);exit(0);}intport=atoi(argv[2]);char*ip=argv[1];intsock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){perror("socket");exit(1);}structsockaddr_inremote;remote.sin_family=AF_INET;remote.sin_port=htons(port);remote.sin_addr.s_addr=inet_addr(ip);intret=connect(sock,(structsockaddr*)&remote,sizeof(remote));charbuf[1024];while(1){printf("pleaseenter:");fflush(stdout);ssize_t_s=read(0,buf,sizeof(buf)-1);buf[_s]='\0';write(sock,buf,sizeof(buf)-1);_s=read(sock,buf,sizeof(buf)-1);//回显if(_s>0){buf[_s]='\0';printf("server-->client:%s",buf);}}return0;}
上面的结果如下:
从上面的结果我我们可以看到,从client端发送的数据,最后被server端hui显给client端了,达到了我们的目的。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。