首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >异步、非阻塞套接字行为. WSAEWOULDBLOCK

异步、非阻塞套接字行为. WSAEWOULDBLOCK
EN

Stack Overflow用户
提问于 2019-03-12 13:19:31
回答 1查看 2.8K关注 0票数 5

我继承了两个应用程序,一个是在Windows 7 PC上运行的Test (客户端),另一个是运行在Windows 10 PC上的服务器应用程序。我试图使用TCP/IP套接字在这两者之间进行通信。客户端向服务器发送请求(XML形式的数据),然后服务器将请求的数据(也是XML)发送回客户端。

设置如下:

代码语言:javascript
复制
       Client                                    Server
--------------------                      --------------------  
|                  |    Sends Requests    |                  |
|   Client Socket  |  ----------------->  |   Server Socket  |
|                  |  <-----------------  |                  |
|                  |      Sends Data      |                  |
--------------------                      --------------------

此过程总是在初始连接(即刚启动的客户端和服务器应用程序)上工作。客户端能够断开与服务器的连接,这将触发套接字的清理。在重新连接时,我几乎总是(这并不总是发生,但大多数情况下)都会收到以下错误:

代码语言:javascript
复制
"Receive() - The socket is marked as nonblocking and the receive operation would block"

此错误将显示在客户端,所讨论的套接字是异步的、非阻塞的套接字。

导致此SOCKET_ERROR的行是:

代码语言:javascript
复制
numBytesReceived = theSocket->Receive(theReceiveBuffer, 10000));

where:
- numBytesReceived is an integer (int)
- theSocket is a pointer to a class called CClientSocket which is a specialisation of CASyncSocket, which is part of the MFC C++ Library.  This defines the socket object which is embedded within the client.  It is an asynchonous, non-blocking socket.
- Receive() is a virtual function within the CASyncSocket object
- theReceiveBuffer is a char array (10000 elements)

在执行上述行时,从函数返回SOCKET_ERROR,并调用theSocket->GetLastError()返回WSAEWOULDBLOCK

SocketTools强调

当非阻塞(异步)套接字试图执行无法立即执行的操作时,将返回错误10035。此错误并不致命,应用程序应将其视为建议。此错误代码对应于Windows套接字错误WSAEWOULDBLOCK。 当从非阻塞套接字读取数据时,如果此时没有更多可用的数据可读取,则将返回此错误。在这种情况下,应用程序应该等待OnRead事件触发,这表明有更多的数据可以读取。IsReadable属性可用于确定是否存在可以从套接字读取的数据。 当将数据写入非阻塞套接字时,如果在等待远程主机读取某些数据时填充本地套接字缓冲区,则将返回此错误。当缓冲区空间可用时,OnWrite事件将触发,这表明可以写入更多的数据。IsWritable属性可用于确定是否可以将数据写入套接字。 需要注意的是,应用程序将不知道在单个写操作中可以发送多少数据,因此,如果客户端试图过快地发送太多数据,则可能会多次返回此错误。如果在发送数据时经常发生此错误,则可能表示网络延迟较高或远程主机无法足够快地读取数据。

我是consistently,得到了这个错误,但是没有接收到套接字上的任何信息。

使用Wireshark,下面的通信将与这里提供的源、目的地和TCP位标志进行通信:

事件:通过TCP/IP将测试管理连接到服务器

代码语言:javascript
复制
Client --> Server: SYN
Server --> Client: SYN, ACK
Client --> Server: ACK

This appears to be correct and represents the Three-Way Handshake of connecting.

SocketSniff confirms that a Socket is closed on the client side.  It was not possible to get SocketSniff to work with the Windows 10 Server application.

事件:从发送数据请求

代码语言:javascript
复制
Client --> Server: PSH, ACK
Server --> Client: PSH, ACK
Client --> Server: ACK

Both request data and received data is confirmed to be exchanged successfully

事件:从服务器断开测试管理

代码语言:javascript
复制
Client --> Server: FIN, ACK
Server --> Client: ACK
Server --> Client: FIN, ACK
Client --> Server: ACK

This appears to be correct and represents the Four-Way handshake of connection closure.

SocketSniff confirms that a Socket is closed on the client side.  It was not possible to get SocketSniff to work with the Windows 10 Server application.

事件:通过TCP/IP将测试管理重新连接到服务器

代码语言:javascript
复制
Client --> Server: SYN
Server --> Client: SYN, ACK
Client --> Server: ACK

This appears to be correct and represents the Three-Way Handshake of connecting.

SocketSniff confirms that a new Socket is opened on the client side.  It was not possible to get SocketSniff to work with the Windows 10 Server application.

事件:从发送数据请求

代码语言:javascript
复制
Client --> Server: PSH, ACK
Server --> Client: ACK

We see no data being pushed (PSH) back to the client, yet we do see an acknowledgement.  

有人知道这里会发生什么事吗?我知道你很难在没有看到源代码的情况下进行诊断,但是我希望其他人可能有过这个错误的经验,并且可以指出我要调查的具体路线。

更多信息:

服务器初始化一个侦听线程并绑定到0.0.0.0:49720。'WSAStartup()‘、'bind()’和‘and ()’函数都返回'0',表示成功。此线程在服务器应用程序的整个生命周期中始终存在。

服务器初始化两个线程,一个读线程和一个写线程。读取线程负责从其套接字读取请求数据,并按照名为Connection的类初始化如下:

代码语言:javascript
复制
HANDLE theConnectionReadThread 
           = CreateThread(NULL,                                    // Security Attributes
                          0,                                       // Default Stacksize
                          Connection::connectionReadThreadHandler, // Callback
                          (LPVOID)this,                            // Parameter to pass to thread
                          CREATE_SUSPENDED,                        // Don't start yet
                          NULL);                                   // Don't Save Thread ID

写入线程是以类似的方式初始化的。

在每种情况下,CreateThread()函数都返回一个合适的句柄。

代码语言:javascript
复制
theConnectionReadThread  = 00000570
theConnectionWriteThread = 00000574  

线程实际上是在以下函数中启动的:

代码语言:javascript
复制
void Connection::startThreads()
{
    ResumeThread(theConnectionReadThread);
    ResumeThread(theConnectionWriteThread);
}                                   

这个函数是从另一个名为ConnectionManager的类中调用的,它管理到服务器的所有可能的连接。在这种情况下,为了简单起见,我只关心一个连接。

将文本输出添加到服务器应用程序显示,在观察错误行为之前,我可以成功地对客户端和服务器进行多次connect/disconnect。例如,在connectionReadThreadHandler()connectionWriteThreadHandler()函数中,一旦它们执行,我就会将文本输出到日志文件中。

当观察到正确的行为时,将向日志文件输出以下行:

代码语言:javascript
复制
Connection::ResumeThread(theConnectionReadThread) returned 1
Connection::ResumeThread(theConnectionWriteThread) returned 1
ConnectionReadThreadHandler() Beginning
ConnectionWriteThreadHandler() Beginning

当观察到错误行为时,将向日志文件输出以下行:

代码语言:javascript
复制
Connection::ResumeThread(theConnectionReadThread) returned 1
Connection::ResumeThread(theConnectionWriteThread) returned 1

回调函数似乎没有被调用。

此时,在客户端上显示错误,表明:

代码语言:javascript
复制
"Receive() - The socket is marked as nonblocking and the receive operation would block"

在客户端,我有一个名为CClientDoc的类,它包含客户端套接字代码。它首先初始化theSocket,它是嵌入在客户端中的套接字对象:

代码语言:javascript
复制
private:
    CClientSocket* theSocket = new CClientSocket;

当客户端和服务器之间初始化连接时,该类将调用一个名为CreateSocket()的函数,该函数的一部分包含在下面,以及它调用的辅助函数:

代码语言:javascript
复制
void CClientDoc::CreateSocket()
{
    AfxSocketInit();
    int lastError;
    theSocket->Init(this);
    
    if (theSocket->Create()) // Calls CAyncSocket::Create() (part of afxsock.h)
    {
        theErrorMessage = "Socket Creation Successful"; // this is a CString
        theSocket->SetSocketStatus(WAITING);             
    }
    else
    {
        // We don't fall in here
    }
}

void CClientDoc::Init(CClientDoc* pDoc)
{
    pClient = pDoc; // pClient is a pointer to a CClientDoc
}

void CClientDoc::SetSocketStatus(SOCKET_STATUS sock_stat)
{
    theSocketStatus = sock_stat; // theSocketStatus is a private member of CClientSocket of type SOCKET_STATUS
}

CreateSocket()之后立即调用SetupSocket(),这里还提供了以下内容:

代码语言:javascript
复制
void CClientDoc::SetupSocket()
{
    theSocket->AsyncSelect(); // Function within afxsock.h
}

当客户端与服务器断开连接时,

代码语言:javascript
复制
void CClientDoc::OnClienDisconnect()
{
    theSocket->ShutDown(2); // Inline function within afxsock.inl
    delete theSocket;
    theSocket = new CClientSocket;
    CreateSocket();
    SetupSocket();        
}

因此,我们删除当前的套接字,然后创建一个新的套接字,该套接字看起来可以正常工作。

此错误正在DoReceive()函数中的客户端上写入。此函数调用套接字尝试读取消息。

代码语言:javascript
复制
CClientDoc::DoReceive()
{
    int lastError;
    switch (numBytesReceived = theSocket->Receive(theReceiveBuffer, 10000))
    {
    case 0:
        // We don't fall in here
        break;
    case SOCKET_ERROR: // We come in here when the faulty behaviour occurs
        if (lastError = theSocket->GetLastError() == WSAEWOULDBLOCK)
        {
            theErrorMessage = "Receive() - The socket is marked as nonblocking and the receive operation would block";
        }
        else
        {
            // We don't fall in here
        }
        break;
    default:
        // When connection works, we come in here
        break;
    }
}

希望添加一些代码证明是有洞察力的。如果需要的话,我应该可以多加一点。

谢谢

EN

回答 1

Stack Overflow用户

发布于 2019-03-12 20:48:05

WSAEWOULDBLOCK错误并不意味着套接字被标记为阻塞。这意味着套接字被标记为非阻塞,并且当时没有可读取的数据。

WSAEWOULDBLOCK意味着如果套接字被标记为阻塞,则套接字会阻塞调用线程,等待数据。

若要了解非阻塞套接字何时有等待读取的数据,请使用Winsock的select()函数或CClientSocket::AsyncSelect()方法请求FD_READ通知,或其他类似的方法。在有东西可读之前,不要尝试阅读。

在您的分析中,您可以看到客户端向服务器发送数据,但服务器没有向客户端发送数据。因此,您的代码中显然有一个逻辑错误,您需要找到并修复它。客户端没有正确地终止其请求,或者服务器没有正确地接收/处理/回复请求。但是,由于您没有显示您的实际代码,所以我们无法告诉您实际发生了什么问题。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55122445

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档