近几日在拜读王艳平老师的网络与通信程序设计(第2版),发现了一个示例代码的BUG.将会导致了winsock程序出现大量的CLOSE_WAIT状态.趁着机会写出来,留给后来学习的新人,大家共勉之.
关于socket关闭时出现CLOSE_WAIT状态的原因,网络上有很多高人分析的资料,大家有兴趣可以搜索一下.我就不班门弄斧了,总而言之一句话,是自身的winsock程序在处理关闭状态的时候没有处理好导致.
该书的51页中的HandleIO函数,原版代码如下:
1 BOOL HandleIO(PTHREAD_OBJ pThread, PSOCKET_OBJ pSocket) 2 { 3 // 获取具体发生的网络事件 4 WSANETWORKEVENTS event; 5 ::WSAEnumNetworkEvents(pSocket->s, pSocket->event, &event); 6 do 7 { 8 if(event.lNetworkEvents & FD_READ) // 套节字可读 9 { 10 if(event.iErrorCode[FD_READ_BIT] == 0) 11 { 12 char szText[256]; 13 int nRecv = ::recv(pSocket->s, szText, strlen(szText), 0); 14 if(nRecv > 0) 15 { 16 szText[nRecv] = '\0'; 17 printf("接收到数据:%s \n", szText); 18 } 19 } 20 else 21 break; 22 } 23 else if(event.lNetworkEvents & FD_CLOSE) // 套节字关闭 24 { 25 break; 26 } 27 else if(event.lNetworkEvents & FD_WRITE) // 套节字可写 28 { 29 if(event.iErrorCode[FD_WRITE_BIT] == 0) 30 { 31 } 32 else 33 break; 34 } 35 return TRUE; 36 } 37 while(FALSE); 38 39 // 套节字关闭,或者有错误发生,程序都会转到这里来执行 40 RemoveSocketObj(pThread, pSocket); 41 FreeSocketObj(pSocket); 42 return FALSE; 43 }
网络中经常有服务器为了避免大量FIN_WAIT1和TIME_WAIT状态的出现,设置socket为发送完数据立即强制关闭,这样就会导致很多socket的FD_CLOSE事件与最后一次FD_READ事件一起到达.因为event.lNetworkEvents是一个按位标识符,这里示例代码使用了else(红色标注)进行处理,所以在遇到这些强制关闭服务端的时候,将会对FD_CLOSE事件漏处理,从而导致大量的CLOSE_WAIT状态产生.
说到这里CLOSE_WAIT的解决办法已经出来了,将红色else部分全部删除,问题解决.