1.select介绍
int select(int nfds, fd_set *restrict readfds,
fd_set *restrict writefds,
fd_set *restrict errorfds,
struct timeval *restrict timeout
);
成功:返回所有sets种的描述符个数:readfds+writefds+errorfds
超时:返回0
出错:返回-1
void FD_ZERO(fd_set *fdset); //清空
void FD_CLR(int fd, fd_set *fdset); //移出
void FD_SET(int fd, fd_set *fdset); //放入
int FD_ISSET(int fd, fd_set *fdset); //判断fd是否在fdset中
select实现说明:
调用select时,通过参数告诉内核(用户感兴趣的IO描述符)
关心IO状态:输入,输出,错误
调用者等待时间
返回之后,内核告诉调用者多个描述符已经准备好了
程序员判断哪些描述符发生变化
调用返回后对准备好的描述符执行读写操作
案例一:回射客户端服务器模型
客户端代码:
使用select监控两个文件描述字:stdin / clisock
#define ERR_EXIT(m) \
do{ \
perror(m); \
exit(EXIT_FAILURE); \
}while(0);
int main(){
int clisock=socket(AF_INET,SOCK_STREAM,0);
if(clisock==-1){
perror("socket");
exit(1);
}
struct sockaddr_in svraddr;
svraddr.sin_family=AF_INET;
svraddr.sin_port=htons(8001);
svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
int ret=connect(clisock,(struct sockaddr*)&svraddr,sizeof(struct sockaddr));
if(ret<0)
ERR_EXIT("connect");
printf("svraddr=%s,port=%d",inet_ntoa(svraddr.sin_addr),ntohs(svraddr.sin_port));
fd_set rset;
FD_ZERO(&rset);
int nready;
int maxfd;
int fd_stdin=fileno(stdin); //int fileno(FILE *stream); 返回stream的整数形式的文件描述符
maxfd=fd_stdin>clisock?fd_stdin:clisock;
char sendbuf[1024]={0};
char recvbuf[1024]={0};
while(1){
FD_SET(fd_stdin,&rset);
FD_SET(clisock,&rset);
nready=select(maxfd+1,&rset,NULL,NULL,NULL);
if(nready==-1){
ERR_EXIT("select");
}
else if(nready==0)
continue;
if(FD_ISSET(fd_stdin,&rset)){ //键盘输入
read(fd_stdin,sendbuf,sizeof(sendbuf));
write(clisock,sendbuf,sizeof(sendbuf));
memset(sendbuf,0,sizeof(sendbuf));
}
if(FD_ISSET(clisock,&rset)){ //closock文件描述符
int ret=read(clisock,recvbuf,sizeof(recvbuf));
if(ret<0){
ERR_EXIT("read");
}
else if(ret==0){
printf("peer close\n");
break;
}
printf(" recv=%s",recvbuf);
memset(recvbuf,0,sizeof(recvbuf));
}
}
return 0;
}