使用非阻塞套接字时,recvfrom() 函数出现错误10035

9

我正在使用ioctlsocket()函数使我的套接字变为非阻塞模式,但是当我调用recvfrom()函数时,我会收到错误10035(WSAEWOULDBLOCK)。

u_long mode = 1;
ioctlsocket(newSocketIdentifier, FIONBIO, &mode);

while(1)
   {
      if((recv_len = recvfrom(newSocketIdentifier, receiveBuffer, sizeof(receiveBuffer), 0, (struct sockaddr *) &clientSocket, &clientSocketLength)) == SOCKET_ERROR)
      {
         char err[128];
         itoa(WSAGetLastError(),err,10);
         MessageBox( NULL,"Could not Receive Data",err,MB_ICONINFORMATION);
         BREAK;
      }
   }

有人能解释一下为什么会发生这种情况吗?:(

I/O 调用可能会失败。您确定已成功将套接字设置为非阻塞吗? - unwind
1
如果当前没有可用数据,则在非阻塞套接字上会收到WSAEWOULDBLOCK错误。这是正常行为。 - Martin R
2个回答

12

如果没有数据可用,这是正常的。 代码是WSAEWOULDBLOCK(参见此表),意味着在阻塞端口上,函数将不得不等待,直到可以被服务。

   while(1)
   {
      if((recv_len = recvfrom(newSocketIdentifier, receiveBuffer, sizeof(receiveBuffer), 0, (struct sockaddr *) &clientSocket, &clientSocketLength)) == SOCKET_ERROR)
      { 
         int ierr= WSAGetLastError();
         if (ierr==WSAEWOULDBLOCK) {  // currently no data available
             Sleep(50);  // wait and try again
             continue; 
         }

         // Other errors
         char err[128];
         itoa(ierr,err,10);
         MessageBox( NULL,"Could not Receive Data",err,MB_ICONINFORMATION);
         break;
      }
   }

谢谢您的回复,但是客户端正在持续从文本文件中读取数据并通过套接字发送,那么为什么没有可供读取的数据呢?:( - Ayse
1
你的短循环将比通过网络到达的数据包更快。如果你收到 WSAEWOULDBLOCK 错误,只需让你的循环沉睡几毫秒(如 Sleep(50)),然后继续执行。 - Grezgory
非常感谢。现在通过使用Sleep()函数,它可以正常工作了,但您认为这是一个好的方法吗?最初我使用WSAWaitforMultipleEvents()函数来调用recvfrom(),只有在FD_READ事件可用时才会调用。我认为这类似于非阻塞套接字,但后来有人给了我使用ioctlsocket()函数的想法。 - Ayse
4
睡眠是一个笨拙的解决方案。通常你会使用select()(或者可能是WSAWaitforMultipleEvents())来等待数据可用,然后尝试读取。或者直接使用阻塞套接字,它会以最佳方式为您等待。 - Grezgory
抱歉问这么愚蠢的问题,但据我理解,如果使用ioctlsocket(),如果没有数据可读,recvfrom()函数不会阻塞并且会无错误地退出 :( 我错了吗? - Ayse

1
我不同意以上所述的“正常”。
在您调用recvfrom时,您将在recv_len中收到一个错误。建议检查该值 - 它将是SOCKET_ERROR,通过调用WSAGetLastError,您将看到错误WSAEWOULDBLOCK
我不是(Windows)套接字专家,但根据我的测试,我无法在非阻塞模式下使用ioctlsocketrecvfrom组合来接收UDP数据(我做了与您示例中相同的事情)。
我计划使用selectrecvfrom的组合,最小可能的超时时间为1微秒。我现在不知道任何其他更好的解决方案。
注意:您还应检查ioctlsocket的返回值以查看可能的错误。
我稍后会提供我的代码示例。
更新(如承诺添加代码):
/* define list of sockets for function select(..) */
fd_set readfds;
/* define timeout for function select(..) */
TIMEVAL tv;
/* timeout: 1us */
tv.tv_usec = 1;
/* timeout: 0s */
tv.tv_sec = 0;
/* just 1 socket is used */
readfds.fd_count = 1;
readfds.fd_array[0] = receivingSocket;
/* determine the status of one or more sockets with timeout */
int selectReturnValue = select(0, &readfds, 0, 0, &tv);
/* check return value of the call of function select(..) */
switch (selectReturnValue)
{
  /* select(..) function timeout */
  case 0:
    /* time limit expired */
    break;
  /* select(..) function error */
  case SOCKET_ERROR:
    /* check the error status for the last windows sockets operation */
    selectError(WSAGetLastError());
    break;
  /* no timeout and no error */
  default:
    /* receive data from UDP */
    resultOfrecvfrom = recvfrom(receivingSocket, receivingBuffer, sizeof(receivingBuffer), 0, (SOCKADDR *)&serverReceptionInfo, &serverReceptionInfoLength);
    /* check result of call of recvfrom(..) */
    switch (resultOfrecvfrom)
    {
      /* connection has been gracefully closed  */
      case 0:
        /* socket was closed */
        break;
      /* socket error occurred during last call of socket operation */
      case SOCKET_ERROR:
        /* check the error status for the last Windows Sockets operation */
        recvfromError(WSAGetLastError());
        break;
      /* resultOfrecvfrom amount of data received */
      default:            
        /* ... add your code here */
        break;
    }
    break;      
}

我刚刚复制了我的代码的关键部分,如果需要更多,请在评论中告诉我。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接