今天發現,用ace封裝的udp莫名其妙的在第一次響應客戶端後,沒法再次響應客戶端的請求。原來是10054的故事,網上有好段子,我的就很少費口舌了。感謝各位大神! html
轉自: http://blog.csdn.net/ccnucjp8136/article/details/4515002 windows
今天碰到了一個很是怪的問題,個人主要功能是先用UDP的Sendto發送一個數據過去,同時啓動一個線程,這個線程去recvfrom來自遠目地主機的包,可是每次我Sendto後,recvfrom不阻塞在那裏,而報10054的錯誤。先貼代碼第一部分:主要是初始化 服務器
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2), &wsaData); 網絡
/*初始化服務端地址和本地客戶端地址並綁定本地套接字*/ socket
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"); this
/*設置客戶端的地址*/
m_ClientAddress.sin_family = AF_INET;
m_ClientAddress.sin_port = htons(0);
m_ClientAddress.sin_addr.s_addr = htons(INADDR_ANY); spa
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;
} .net
/*將客戶機的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都會向一些已經關閉的客戶發包,浪費了服務器資源;只能經過其餘機制肯定客戶端已經斷開,延遲一段時間後才能肯定哪一個客戶端出問題。