或者你会发现,你在编写程序时,不注意,或者一个很不起眼的写法,可它会给你带来很无奈的错误,让你很久才会发现,原来这个问题是这么回事。
1、常见的写法问题
1
2
3
4
5
6
7
8
9
|
fd_set fd;
FD_ZERO(&fd);
FD_SET( skt, &fd);
do
{
int
n = select( skt+1, &fd, 0,0, 0);
if
( n == -1 )
break
;
else
if
( n > 0)
break
;
}
while
(
true
);
|
这个问题看似不怎么,根据原理select会重置参数,所以fd这个参数在调用完select之后会被更改,所以下次执行select必须重新设置fd,就是说要将 FD_ZERO(&fd); FD_SET( skt, &fd);放在do,while里面,即每次select都要先初始化fdset;
2、另外做超时时可能会出现115错误,linux下面使用常见错误,这个问题我也遇到过,发现是一个很犀利的写法
select最后一个参数是timeval指针,这个指针也需要和fd一样,在每次select都要重新设置,如下面代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//ERROR
timeval tv; tv.sec = 0;tv.usec = 10;
do
{
fd_set fd; FD_ZERO(&fd); FD_SET(skt,&fd);
int
n = select( skt + 1, &fd, 0,0, &tv );
//执行完这句tv就被置0了
if
( n ==-1 || n>0)
break
;
}
while
(
true
);
//OK
do
{
timeval tv; tv.sec = 0;tv.usec = 10;
fd_set fd; FD_ZERO(&fd); FD_SET(skt,&fd);
int
n = select( skt + 1, &fd, 0,0, &tv );
//执行完这句tv就被置0了
if
( n ==-1 || n>0)
break
;
}
while
(
true
);
|
这是我在写一个超时计算代码时发现的问题,有一个函数发送数据,需要在指定的时间内发送完,如果超过时间就返回真实发送长度。
那么,你可能需要在多次send之间来计算哪些是超时的,假如我传一个30秒的超时,你不能只select一次,将时间设置为超时,因为这样有问题,就是在第一次select成功之后,在send的时候可能没有发送完所有数据,还要不断的发送,在再次发送时超时就不好计算了,因为你上次select用的30秒,把时间用光了,并且你也不知道select到底用了多少时间。
我们可以将30秒划分为几个小的秒单位,比较每次select用1秒的时间,这样每次select或send超时都计1秒时间 ,在不断的累加就可以了。刚好在累加的时候如果你把timeval参数写在 do,while外面
那么只有第一次是正常的,第二次timeval变成 tv.sec = 0,tv.usec = 0;这样会造成select超时计算失误,因为tv值0表示不等待直接返回,假如此时发送缓冲已满,立马返回超时,这样你累加1秒时间就不准。
我当时测试 的是,设置30秒,结果只等 了一秒就返回超时了,打印日志显示select返回0确实是有30次,可是除了第一次有延迟,后面的基本上是同时显示出来的,就说明后面select根本没有等待,后来发现这个timeval被置0了,我呢个去,不发现不知道,一发现太肯我了。