为什么 select() 返回 1 而 recv() 返回 0?

Why is select() returning 1 but recv() returning 0?

我可以清楚地看到 recvbuf 拥有我期望的所有数据,但 select() 一直返回 1。

现在它陷入了 else if (iBuffer == 0) {} 的边缘。

SOCKET m_ConnectSocket;
/* The socket setup is done elsewhere but just adding this for clarity
   This socket is responsible for sending from the client to the server
   and also receives anything the server sends back.

   This socket is doing the connect() & initial send()
*/

fd_set set;
struct timeval timeout;

// Set up the file descriptor set.
FD_ZERO(&set);
FD_SET(m_ConnectSocket, &set);

// Set up the struct timeval for the timeout.
timeout.tv_sec  = RECV_DELAY_SEC;
timeout.tv_usec = RECV_DELAY_USEC;

int iBuffer = 0;

do
{
    iResult = select(m_ConnectSocket, &set, NULL, NULL, &timeout);
    if (iResult > 0)
    {
        iBuffer = recv(m_ConnectSocket, recvbuf, DEFAULT_BUFLEN, 0);
        if (iBuffer > 0)
        {
            string sRecv(recvbuf);

            STrace = String::Format("Bytes Received: {0}", iBuffer);
            Trace(STrace, TRACE_INFO);

            STrace = String::Format("Data Received: [{0}]", gcnew String(sRecv.c_str()));
            Trace(STrace, TRACE_INFO);
        }
        else if (iBuffer == 0)
        {
            STrace = String::Format("iBuffer empty");
            Trace(STrace, TRACE_INFO);
        }
        else
        {
            STrace = String::Format("recv failed: {0}", WSAGetLastError());
            Trace(STrace, TRACE_ERROR);
        }
    }
    else if (iResult == 0)
    {
        STrace = String::Format("No data left in buffer");
        Trace(STrace, TRACE_INFO);

        pMessage->Data(recvbuf);
        if (iSentType != pMessage->Type())
        {
            STrace = String::Format("Message type mismatch: {0} | Expected: {1}", (int)pMessage->Type(), (int)iSentType);
            Trace(STrace, TRACE_WARNING);
        }
    }
    else if (iResult == -1)
    {
        STrace = String::Format("select() error");
        Trace(STrace, TRACE_ERROR);
    }
} while (iResult > 0);

由于 select 将其参数作为指针传递,并且这些数据结构被 select put

更改
fd_set set;
struct timeval timeout;

// Set up the file descriptor set.
FD_ZERO(&set);
FD_SET(m_ConnectSocket, &set);

// Set up the struct timeval for the timeout.
timeout.tv_sec  = RECV_DELAY_SEC;
timeout.tv_usec = RECV_DELAY_USEC;

就在 select 语句之前,即在循环内。

您在滥用 select()。请阅读 documentation:

Parameters

nfds [in]
Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.

...

Upon return, the structures are updated to reflect the subset of these sockets that meet the specified condition.

因此,每次在循环中调用 select() 时,您 必须 重置 fd_set 结构。

此外,看起来 select() 超时时,您正在尝试解析收到的任何内容,但您只是在解析最后一次成功 recv() 返回的最后一个缓冲区,如果任何。如果recv()在数据超时之前必须多次调用,则需要收集每个返回的缓冲区,然后将它们作为一个整体一起解析。

此外,您的一般错误处理也可以使用一些改进。

尝试更像这样的东西:

fd_set set;
struct timeval timeout;

string sBuffer;
int iBuffer;

do
{
    // Set up the file descriptor set.
    FD_ZERO(&set);
    FD_SET(m_ConnectSocket, &set);

    // Set up the struct timeval for the timeout.
    timeout.tv_sec  = RECV_DELAY_SEC;
    timeout.tv_usec = RECV_DELAY_USEC;

    iResult = select(0, &set, NULL, NULL, &timeout);
    if (iResult > 0)
    {
        iBuffer = recv(m_ConnectSocket, recvbuf, DEFAULT_BUFLEN, 0);
        if (iBuffer > 0)
        {
            string sRecv(recvbuf, iBuffer);

            STrace = String::Format("Bytes Received: {0}", iBuffer);
            Trace(STrace, TRACE_INFO);

            STrace = String::Format("Data Received: [{0}]", gcnew String(sRecv.c_str()));
            Trace(STrace, TRACE_INFO);

            sBuffer += sRecv;
        }
        else
        {
            if (iBuffer == 0)
            {
                STrace = String::Format("Connection closed");
                Trace(STrace, TRACE_INFO);
            }
            else
            {
                STrace = String::Format("recv failed: {0}", WSAGetLastError());
                Trace(STrace, TRACE_ERROR);
            }
            break;
        }
    }
    else if (iResult == 0)
    {
        STrace = String::Format("No data left in buffer");
        Trace(STrace, TRACE_INFO);

        pMessage->Data(sBuffer.c_str());
        if (iSentType != pMessage->Type())
        {
            STrace = String::Format("Message type mismatch: {0} | Expected: {1}", (int)pMessage->Type(), (int)iSentType);
            Trace(STrace, TRACE_WARNING);
        }
    }
    else
    {
        STrace = String::Format("select failed: {0}", WSAGetLastError());
        Trace(STrace, TRACE_ERROR);
    }
}
while (iResult > 0);

到目前为止,所有关于重置 FD 集的答案都是正确的,但其中 none 实际上已经确定了根本问题。

如果recv() returns 为零,则表示对端已断开连接,您必须关闭套接字。如果你不这样做,你将继续 select 套接字可读并继续接收零。

不是意味着'buffer empty'。