今天发现,用ace封装的udp莫名其妙的在第一次响应客户端后,无法再次响应客户端的请求。原来是10054的故事,网上有好段子,个人就不多费口舌了。感谢各位大神!
转自: http://blog.csdn.net/ccnucjp8136/article/details/4515002
今天碰到了一个非常怪的问题,我的主要功能是先用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);
}
}
真是受教了,这个问题调了我好久。
转自 : http://windows.chinaitlab.com/soft/890969.html
因为这个问题,我很想抽微软。网上其他人有类似的问题,我也碰到了,没有很好的解决方案。这个问题是Winsock所特有的(最起码所有的问题都是在Windows平台下)。一般在使用UDP Socket时,我们发出去数据,是不管它到不到达的,而且UDP并不是面向连接的,所以在收到这个错误时,会感觉很奇怪:"连接被重置10054".
这个错误一般是使用UDP Socket接受时收到(这里我不讲具体的语言了,不管你用C#、Python还是C,在Windows下都会有类似的问题,只要你调用类似ReceiveFrom函数)。这是上一次Send操作向一个地址发送,但是那个地址没有Socket监听(例如对等体崩溃),那么ICMP控制协议会向我们发送一个Port Unreachable错误,当然这个错误应该包含对方的详细地址等信息,但是Winsock把这个错误转化为Connection Reset,在你下一次调用读操作的时候,引发异常,却没把详细信息给你--例如用C#接受到的对方地址是0.0.0.0.
而这个问题最要命的是,如果你不采取措施,每次调用读操作都会引发该异常!
唯一恢复正常的办法就是把Socket关掉,重开。
这就非常要命:你要实现一个UDP服务器,把收到的音频发给所有的客户,如果某个客户崩溃了或者网络不好,你的Send不会出问题,但是你Receive的时候却出了问题!好吧,你捕获了异常,重新Receive,还是异常!好吧,你关掉Socket重新建立,但是因为不知道是哪个客户出了问题,所以不能及时把他的地址从发送列表里去除(即使使用心跳检测也要等几秒种),下次Send还是这样,你就不停地关闭创建Socket,谁受的了?
网络上的讨论,最后要么说这是一个bug,要么使用如下代码(以C#为例)
const int SIP_UDP_CONNRESET=-1744830452;
socket.IOControl(SIP_UDP_CONNRESET, new byte[] {0, 0, 0, 0}, null);
设置这个选项忽略那个Reset错误,这样不用重建Socket,只不过每次Socket都会向一些已经关闭的客户发包,浪费了服务器资源;只能通过其他机制确定客户端已经断开,延迟一段时间后才能确定哪个客户端出问题。