UDP 10054 的故事

      今天發現,用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都會向一些已經關閉的客戶發包,浪費了服務器資源;只能經過其餘機制肯定客戶端已經斷開,延遲一段時間後才能肯定哪一個客戶端出問題。

相關文章
相關標籤/搜索