今天碰到了一个非常怪的问题,我的主要功能是先用UDP的Sendto发送一个数据过去,同时启动一个线程,这个线程去recvfrom来自远目地主机的包,但是每次我Sendto后,recvfrom不阻塞在那里,而报10054的错误。先贴代码
第一部分:主要是初始化
WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &wsaData);
/*初始化服务端地址和本地客户端地址并绑定本地套接字*/
m_nPort = GetConfigValue("BIPSERVERCONFIG", "CATPORT", 8888);//获取服务目的端口 m_ServerAdd = GetConfigValue("BIPSERVERCONFIG" ,"CATADDRESS", "127.0.0.1");//获取服务目的端口
m_ServerAddress.sin_family = AF_INET; m_ServerAddress.sin_port = htons(m_nPort); m_ServerAddress.sin_addr.s_addr = inet_addr(m_ServerAdd); //m_ServerAddress.sin_addr.s_addr = inet_addr("10.8.4.52");
/*设置客户端的地址*/ m_ClientAddress.sin_family = AF_INET; m_ClientAddress.sin_port = htons(0); m_ClientAddress.sin_addr.s_addr = htons(INADDR_ANY);
m_ClientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(m_ClientSocket == SOCKET_ERROR) { DWORD dwError = GetLastError(); TRACE("错误码:%d", dwError); WriteRunLog_EX("InitSocket","初始化套接字错误\\n"); return FALSE; }
/*将客户机的socket和客户机的Socket地址bind*/ int ret = 0; ret = bind(m_ClientSocket, (struct sockaddr*)&m_ClientAddress, sizeof(m_ClientAddress)); if (ret != 0) { DWORD dwError = GetLastError(); TRACE("错误码:%d", dwError); WriteRunLog_EX("InitSocket","bind套接字错误\\n"); return FALSE; }
第二部分:Sendto处代码:从这里你可以看到,我Sendto一个包给远端目的主机马上启动一个线程去收数据。
retCode = sendto(m_ClientSocket, lpBuf,len, 0,(struct sockaddr*)&m_ServerAddress, sizeof(m_ServerAddress)); if (SOCKET_ERROR == retCode) { return FALSE; }
///int addressLen = sizeof(addr); //retCode = recvfrom(m_ClientSocket, lpBuf, len, 0, (struct sockaddr*)&addr, &addressLen );
/*发送数据后,等待远端服务端发送数据过来*/ pRecvThread = AfxBeginThread((AFX_THREADPROC)StartRecvProc,this,THREAD_PRIORITY_NORMAL); if (NULL == pRecvThread) { TRACE("启动接收线程失败!"); WriteRunLog_EX("StartRecv","bind错误\\n"); return FALSE;
}
第三部分:收线程的代码
while (TRUE) { int addressLen = sizeof(struct sockaddr); retCode = recvfrom(m_ClientSocket, data, sizeof(data), 0, (struct sockaddr*)&m_ServerAddress, &addressLen ); if (SOCKET_ERROR == retCode) { DWORD dwError = GetLastError(); TRACE("错误码为:%d",dwError); TRACE("收包错误!"); WriteRunLog_EX("StartRecv","收包错误\\n"); } else { char testBuf[8192]; memcpy(testBuf, data, retCode); testBuf[retCode] = '\\0'; TRACE("StartRecv收到包:%s",testBuf); BIP_CATMsg msg; msg.len = retCode; memcpy(msg.strBuf, data, msg.len); m_RecvMsgQueue.AddTail(msg); } }
我的本意在这个recvfrom处应会阻塞,但是没有,GetLastErrorr后显示10054,我在网上查了一下,原来是winsock自已的bug。具体原因是:http://support.microsoft.com/kb/263823/
If sending a datagram using the sendto function results in an "ICMP port unreachable" response and the select function is set for readfds, the program returns 1 and the subsequent call to the recvfrom function does not work with a WSAECONNRESET (10054) error response. In Microsoft Windows NT 4.0, this situation causes the select function to block or time out.
解决办法:在我第一部代码初始化后加入如下代码。
DWORD dwBytesReturned = 0; BOOL bNewBehavior = FALSE; DWORD status;
#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) // disable new behavior using // IOCTL: SIO_UDP_CONNRESET status = WSAIoctl(m_ClientSocket, SIO_UDP_CONNRESET, &bNewBehavior, sizeof(bNewBehavior), NULL, 0, &dwBytesReturned, NULL, NULL);
if (SOCKET_ERROR == status) { DWORD dwErr = WSAGetLastError(); if (WSAEWOULDBLOCK == dwErr) { // nothing to do return(FALSE); } else { printf("WSAIoctl(SIO_UDP_CONNRESET) Error: %d\\n", dwErr); return(FALSE); } }
真是受教了,这个问题调了我好久。
(陈俊平) |