分类: LINUX
2009-08-10 15:00:11
liRet = select(maxfdp1, &rset, NULL, NULL, &timeout);
if( 0 >= liRet ) {
continue;
}
if( FD_ISSET( liListenSock, &rset ) ) {
// accept socket
connfd = accept(liListenSock, (struct sockaddr *)&cliaddr, &clilen);
if( 0 >= connfd ) {
continue;
}
fcntl( connfd, F_SETFL, O_NONBLOCK | fcntl( connfd, F_GETFL, 0 ) );
if( maxfdp1 <= connfd ) {
maxfdp1 = connfd + 1;
}
FD_SET( (unsigned int)connfd, &rsetTemp );
connfds[ connfd ] = connfd;
liRet--;
}
// recv data
for( n = 1; n < sizeof( connfds ) / sizeof( int ) && liRet > 0; ++n ) {
if( connfds[ n ] > 0 && FD_ISSET( connfds[n], &rset) ) {
if(0 != RecvPack( connfds[ n ] , ....) ) {
// socket失败或者对方已经关闭,这里就清除,关闭socket
FD_CLR( (unsigned int)connfds[ n ], &rsetTemp );
close( connfds[ n ] );
connfds[ n ] = 0;
}
liRet--;
}
}
}
}
大家注意深蓝色的代码,能保证socket正确的关闭吗?那要看情况了。
分析一种可能:因为数据收发的socket是非阻塞的,那么当网络上没有数据时,我们去调用一次recv(),
recv()将返回-1,并且设置全局变量errno=11(EWOULDBLOCK ) ,接着可能还接收数据, 我们关心是最后client发起关闭,我们server调用recv()返回0,能不能走return -1; 分支而正常退出呢?不一定, 因为有UNIX常识的人知道,errno是一个全局变量,表示最近一次错误码,如果最近一次还是11 (EWOULDBLOCK ) , 就是从上次调用被设置后,还没有被其它系统函数设置新的其它值,那么程序是走return 0; 在Startup()里是关闭不掉的!
给我们的教训是:
1。写程序要规范.
对recv()返回值的判断要分三步走, 返回0表示对方已经主动关闭,我们就不要判断errno的值了。
2。全局变量要小心:
UNIX里的errno, windows里的GetLastError(), 其实都是进程级的全局变量, 很多系统调用会改写他的,对他的判断依赖,你要想清楚他们的真实含义。
3。在自己的程序里少用全局变量,局部静态变量。
当然不是说你不能用,而是想清楚,该不该用,有更好的解决方法没有。