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

时间:2016-02-19 21:31:27

标签: c++ windows select networking winsock2

我清楚地看到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);

3 个答案:

答案 0 :(得分:5)

到目前为止,他们所说的关于重置FD集的所有答案都是正确的,但是他们都没有真正确定了潜在的问题。

如果int openssl_fetch(char *request, HttpResponse *response) { char *HOST_NAME = "abc.com"; // Substituted for privacy char *HOST_PORT = "443"; long res = 1; int ret = 1; unsigned long ssl_err = 0; SSL_CTX* ctx = NULL; BIO *web = NULL, *out = NULL; SSL *ssl = NULL; do { /* Internal function that wraps the OpenSSL init's */ /* Cannot fail because no OpenSSL function fails ??? */ init_openssl_library(); /* https://www.openssl.org/docs/ssl/SSL_CTX_new.html */ const SSL_METHOD* method = SSLv23_method(); ssl_err = ERR_get_error(); //ASSERT(NULL != method); if (!(NULL != method)) { print_error_string(ssl_err, "SSLv23_method"); break; /* failed */ } /* http://www.openssl.org/docs/ssl/ctx_new.html */ ctx = SSL_CTX_new(method); /* ctx = SSL_CTX_new(TLSv1_method()); */ ssl_err = ERR_get_error(); //ASSERT(ctx != NULL); if (!(ctx != NULL)) { print_error_string(ssl_err, "SSL_CTX_new"); break; /* failed */ } /* https://www.openssl.org/docs/ssl/ctx_set_verify.html */ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback); /* Cannot fail ??? */ /* https://www.openssl.org/docs/ssl/ctx_set_verify.html */ SSL_CTX_set_verify_depth(ctx, 5); /* Cannot fail ??? */ /* Remove the most egregious. Because SSLv2 and SSLv3 have been */ /* removed, a TLSv1.0 handshake is used. The client accepts TLSv1.0 */ /* and above. An added benefit of TLS 1.0 and above are TLS */ /* extensions like Server Name Indicatior (SNI). */ const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION; long old_opts = SSL_CTX_set_options(ctx, flags); UNUSED(old_opts); /* http://www.openssl.org/docs/ssl/SSL_CTX_load_verify_locations.html */ // res = SSL_CTX_load_verify_locations(ctx, "random-org-chain.pem", NULL); const char *file_loc = "C:\\Users\\jshackelford\\Desktop\\trusted-roots.pem"; //const char *file_loc = "/C/Users/jshackelford/Desktop/trusted-roots.pem"; //Tried both res = SSL_CTX_load_verify_locations(ctx, file_loc, NULL); ssl_err = ERR_get_error(); } return 0; } 返回零,则表示对等方已断开连接,您必须关闭套接字。如果你不这样做,你将继续选择套接字可读并继续接收零。

意味着'缓冲区空白'。

答案 1 :(得分:2)

由于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陈述之前,即在循环内。

答案 2 :(得分:1)

您滥用select()。请阅读documentation

  

参数

     

nfds [in]
  的忽略即可。仅包含nfds参数以与Berkeley套接字兼容。

     

...

     

返回时,结构会更新以反映符合指定条件的这些套接字的子集。

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

此外,看起来当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);
相关问题