之前一直困扰于select函数的用法, 不明白他到底何用,后来自己动手写了C/S框架,
才慢慢对select函数有了一点点的了解,所以今天加班整理出来,方便大家一起学习讨论。
对于select函数的原型以及参数我就不多说了,可以在终端 man select 就清楚了。
直接用代码解释吧
while(1){
//
cli_sock = accept(ser_sock, (struct sockaddr *)& clieaddr_in, &socklen);
if( cli_sock < 0 )
{
perror("accept");
return -1;
//其他读写操作
}
最先我服务端代码就直接这样写了,当客户端连上一个后能正常工作,但是当有第二个客户端连上了,
就出问题了,因为下面的读写操作我也是一个while(1)循环来做接收处理的,这样的话就一直阻塞在
read这个函数了,这个时候才想起来了select函数的妙处所在,所以立马修改了代码:
// do something
FD_ZERO(&fd_r);
FD_ZERO(&fd_w);
FD_SET(ser_sock, &fd_r);
FD_SET(ser_sock, &fd_w);
tvv.tv_sec = 2;
tvv.tv_usec = 0;
while(1)
{
//用select()函数进行accept是否有客户端连接到来的轮询,否则程序会可能一直在这里阻塞,
//当没有客户端到来的时候,用户层不能进行处理
Ret = select(ser_sock + 1, &fd_r, &fd_w, NULL, &tvv);
printf("\n [%s]-[%d]: Ret = %d \n", __func__, __LINE__, Ret);
switch( Ret )
{
case -1:
case 0:
continue;
default:
break;
}
cli_sock = accept(ser_sock, (struct sockaddr *)& clieaddr_in, &socklen);
if( cli_sock < 0 )
{
perror("accept");
return -1;
}
while(1)
{
// do something
}
}
重新修改了服务端代码之后,在用客户端去连的时候, 一个问题又出来了,
select函数会立马返回,而且一直在打印select的返回值为0 也就是没有准
备好的写或者读的套接字,我当时也比较郁闷了,明显有套接字连上去的啊,
为什么会没有准备好的呢?此时想到了阻塞时间的那个参数,我就改成了10秒,
结果就可以的,但是如果客户端过了10秒之后再去连接的时候,又出现了相同的
问题了,百思不得其解,回头在去看man手册,才恍然大悟过来:
On Linux, select() modifies timeout to reflect the amount of time not slept; most other implementations do not do this. (POSIX.1-2001 permits either behavior.)
This causes problems both when Linux code which reads timeout is ported to other operating systems, and when code is ported to Linux that reuses a struct timeval for
multiple select()s in a loop without reinitializing it. Consider timeout to be undefined after select() returns.
对于英文我不敢妄动的去说的真正意思,但是我只能片面的说说我的理解:
select函数会修改timeout的值还反应还没有睡眠的时间,(就这一句话就足一解决了上面的问题了,也到了select的门道)。
后面的意思我就不多解释,主要就是代码移植可能照成的阻塞时间的修改之类的。
原来每次返回select都会修改阻塞的时间,所以你在while外面给select赋值阻塞时间值的话,每次循环都会消耗掉相关阻塞时间的,
最后在阻塞时间为0时,就一直在continue,客户端也就无法连接进来了,同时,相关fd_set这些 读写,异常的 检查套接字也是会改变的,
所以每次循环都是必须重新作赋值操作才是有效的,不然会出现无法预料的意外
知道上面的错误后们重新修改代码后:
int Ret = -1;
fd_set fd_r,fd_w;
struct timeval tvv;
while(1)
{
//用select()函数进行accept是否有客户端连接到来的轮询,否则程序会可能一直在这里阻塞,
//当没有客户端到来的时候,用户层不能进行处理
FD_ZERO(&fd_r);
FD_ZERO(&fd_w);
FD_SET(ser_sock, &fd_r);
FD_SET(ser_sock, &fd_w);
tvv.tv_sec = 2;
tvv.tv_usec = 0;
Ret = select(ser_sock + 1, &fd_r, &fd_w, NULL, &tvv);
printf("\n [%s]-[%d]: Ret = %d \n", __func__, __LINE__, Ret);
switch( Ret )
{
case -1:
case 0:
continue;
default:
break;
}
cli_sock = accept(ser_sock, (struct sockaddr *)& clieaddr_in, &socklen);
if( cli_sock < 0 )
{
perror("accept");
return -1;
}
//do something
}
这样之后,果然,问题基本解决了或许还是我懂的太少!
开始搞网络编程也就半年时间左右,APUE还没看完,偶尔用时才翻一翻
以上就是我对select函数的理解,可能只是冰山一角,但是也拿出来大家分享一下,
有什么说的不周到的地方,大家可以指点一二,大家相互学习,相互进步!