您的位置:首页 > 理论基础 > 计算机网络

select用法详解 http://blog.chinaunix.net/uid-23373524-id-2426940.html

2015-07-30 19:13 666 查看
select用法详解 2010-04-09
21:06:49

分类: LINUX

今天弄了下网络编程,为了让套接字不阻塞采用了select的方法。下面结合unix环境高级编程及自己实际使用时遇到的问题解释下select用法。

#include

int select(int maxfdp1,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

先说最后一个参数,它指定愿意等待的时间。
struct timeval
{
long tv_sec;
long tv_usec;
};
有3种情况:
timeout == NULL
永远等待。如果捕捉到一个信号则中断此无限期等待。当所指定的描述符中的一个已准备好或捕捉到一个信号则返回。如果捕捉到一个信号,则select返回-1,errno设置为EINTR.

timeout->tv_sec = 0 && timeout->tv_usec = 0
完全不等待。测试所有指定的描述符并立即返回。这是得到多个描述符的状态而不阻塞select函数的轮询方法。

timeout->tv_sec != 0 || timeout->tv_usec != 0

等待指定的秒数和微秒数。

注意:
(1)千万不要混淆了timeout == NULL与timeout->tv_sec = 0 && timeout->tv_usec = 0这2种情况,结果截然不同。
(2)timeout->tv_sec != 0 || timeout->tv_usec != 0这种情况下,超时过后,就变成了
timeout->tv_sec = 0 && timeout->tv_usec = 0。

中间的三个参数readfds,writefds和exceptfds是指向描述符集的指针。这三个描述符集说明了我们关心的可读、可写或处于异常条件的各个描述符。每个描述符放在一个fd_set数据类型中。这种数据类型为每一可能的描述符保持了一位。

对fd_set数据类型可以进行处理的是:分配一个这种类型的变量;将这种类型的一个变量赋予同类型的另一个变量;或对于这种类型的变量使用下列四个函数中的一个。

#include
int FD_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(int fd,fd_set *fdset);

这些接口可实现为宏或函数。具体意见就不多说了。

int select(int maxfdp1,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

因为描述符编号从0开始,所以要在最大描述符编号值上加1.第一个参数实际上是要检查的描述符数(从描述符0开始)
select有三个可能的返回值。
(1)返回值-1表示出错。出错是有可能的,例如在所指定的描述符都没有准备好时捕捉到一个信号。在此种情况下,将不修改其中任何描述符集。
(2)返回值0表示没有描述符准备好。若指定的描述符都没有准备好,而且指定的时间已经超过,则发生此种情况。此时,所有描述符集皆被清0.
(3)正返回值表示已经准备好的描述符数。

注意红色部分,超时后,每次描述符集都需要重新设置。

#include

#include

#include

#include

#include

#include
int main(int argc,char *argv[])

{

int listen_fd;

int client_fd;

socklen_t clt_len;

struct sockaddr_in srv_addr;

struct sockaddr_in clt_addr;

int port;

int ret;

int len;

int num;

char recv_buf[1024];

fd_set read_fds;

struct timeval wait_time;
if(argc != 2)

{

printf("Usage: %s port_name\n",argv[0]);

return 1;

}

port = atoi(argv[1]);
listen_fd = socket(PF_INET,SOCK_STREAM,0);

if(listen_fd < 0){

perror("cannot create socket");

return 1;

}
memset(&srv_addr,0,sizeof(srv_addr));

srv_addr.sin_family=AF_INET;

srv_addr.sin_addr.s_addr=htonl(INADDR_ANY);

srv_addr.sin_port=htons(port);
ret = bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));

if(ret < 0)

{

perror("cannot bind the socket");

return 1;

}
ret = listen(listen_fd,1);

if(ret == -1)

{

perror("cannot listen the client connect request");

close(listen_fd);

return 1;

}
wait_time.tv_sec=0;

wait_time.tv_usec=0;

FD_ZERO(&read_fds);

FD_SET(listen_fd,&read_fds);

while(1)

{

FD_ZERO(&read_fds);

FD_SET(listen_fd,&read_fds);

ret = select(listen_fd + 1,&read_fds,NULL,NULL,&wait_time);

if(ret > 0)

{

if(FD_ISSET(listen_fd,&read_fds) > 0)

{

len = sizeof(clt_addr);

client_fd = accept(listen_fd,(struct sockaddr*)&clt_addr,&len);

if(client_fd < 0)

{

perror("cannot accept client connect request");

close(listen_fd);

return 1;

}
while(1)

{

FD_ZERO(&read_fds);

FD_SET(client_fd,&read_fds);

if(select(client_fd + 1,&read_fds,NULL,NULL,&wait_time) > 0)

{

if(FD_ISSET(client_fd,&read_fds) > 0)

{

len = read(client_fd,recv_buf,sizeof(recv_buf));

if(len > 0)

{

recv_buf[len] = 0;

printf("%s\n",recv_buf);

write(client_fd,recv_buf,len);

}

else

{

close(client_fd);

break;

}

}

}

}

}

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: