本来不想吐槽的了,但是想到会有很多初学者犯了我所犯过的低级错误,找bug找了一天都找不到,还是决定写写这个比较隐性的错误把。(ps:本人菜鸟一枚)
如下典型的select多路复用套接口代码
fd_set rset;
int socket_one, socket_two, maxfd;
/* 一些列的初始化代码 */
maxfd = socket_one > socket_two ? socket_one: socket_two;
FD_ZERO(&rset);
FD_SET(socket_one, &rset);
FD_SET(socket_two, &rset);
while(1){
select(maxfd + 1, &rset, NULL, NULL, NULL);
if(FD_ISSET(socket_one, &rset)){
/* 代码*/
}
if(FD_ISSET(socket_two, &rset)){
/*代码*/
}
}
一眼看上去似乎没有么多大的问题,但是把程序编译跑起来之后(假如你预计select第一次返回,socket_one可读,select第二次返回socket_two可读),情况并不如你所想。第二次当socket_two可读的时候,程序根本就不去读socket_two套接口!
出现这个隐性bug的原因在于select第一次返回的时候,只有socket_two在rset集合里面。因此当程序再次select的时候,相当于
FD_ZERO(&rset);
FD_SET(socket_one, &rset);
select(maxfd + 1, &rset, NULL, NULL, NULL);
也就是说第二次的select只是检测socket_one是否可读,socket_two是否可读已经不能检测到!正确的代码应该如下:
fd_set rset, allset;/*添加一个allset的变量,存储所有想要检查的套接口*/
int socket_one, socket_two, maxfd;
/* 一些列的初始化代码 */
maxfd = socket_one > socket_two ? socket_one: socket_two;
FD_ZERO(&allset);
FD_SET(socket_one, &allset);
FD_SET(socket_two, &allset);
while(1){
rset = allset;/*每次循环都应恢复,这一步是最关键的*/
select(maxfd + 1, &rset, NULL, NULL, NULL);
if(FD_ISSET(socket_one, &rset)){
/* 代码*/
}
if(FD_ISSET(socket_two, &rset)){
/*代码*/
}
}