【问题标题】:OpenSSL Non-Blocking Socket SSL_read() unpredictableOpenSSL 非阻塞套接字 SSL_read() 不可预测
【发布时间】:2015-09-19 04:55:27
【问题描述】:

我正在尝试使用 OpenSSL 创建一个非阻塞套接字,这样我可以循环 SSL_read() 直到没有更多数据,然后中断循环。经过大量工作,我终于使连接正常工作,但现在前一千多次迭代 SSL_read() 将返回 -1。之后,它会给我套接字上的实际数据。 SSL_read() 也没有返回准确的读取字节数,即使它正在读取正确的字节,它也总是说 -1。我能够通过阻塞套接字使其完美工作,但非阻塞似乎有问题......

char *sslRead (connection *c)
{
const int readSize = 1024;
char *rc = NULL;
int r;
int received = -1, count = 0;
int TotalReceived = 0, ReallocSize = 0;
char buffer[1024];

if (c)
{
    while (1)
    {

        received = SSL_read (c->sslHandle, buffer, readSize);

    buffer[received] = '\0';

        TotalReceived += received;
        printf("Buffsize - %i - %s \n", received, buffer);


        if (received <= 0)
        {

            // this line added per advice of Eric Tsui but does not
            // change behaviour 
            received = SSL_read (c->sslHandle, buffer, readSize);



            //printf(" received equal to or less than 0\n")
            switch (SSL_get_error(c->sslHandle, received))
            {
                case SSL_ERROR_NONE:
                {
                        printf("SSL_ERROR_NONE %i\n", count);
                        //if (received != -1)
                        //  goto END;
                        break;
                }   
                case SSL_ERROR_ZERO_RETURN: 
                {
                        printf("SSL_ERROR_ZERO_RETURN %i\n", count);
                        goto END;
                        break;
                }   
                case SSL_ERROR_WANT_READ: 
                {
                        printf("SSL_ERROR_WANT_READ %i\n", count);

                        break;
                }
                case SSL_ERROR_WANT_WRITE: 
                {
                        printf("SSL_ERROR_WANT_WRITE %i\n", count);
                        goto END;
                        //break;
                }
                default:
                {
                        printf("error %i\n", received); 
                        break;
                }
            }     


            count++;
        }
    }
}
END:
return rc;
}

如果您以前这样做过,如果您能告诉我这段代码有什么问题,我将不胜感激。谢谢。

【问题讨论】:

  • 如果你只是要循环阅读,为什么需要它是非阻塞的?你有什么更好的事情要做吗?
  • 因为如果不是 SSL_read() 将阻塞(我没有要搜索的行尾字符)并且程序执行停止。它需要是非阻塞的......
  • 你必须在received = SSL_read (c-&gt;sslHandle, buffer, readSize);之后立即检查if (received &lt;= 0)
  • 感谢 Eric 的帮助,但我做到了,它仍然在无限循环。如何知道最后一个字节已被读取?
  • 如果SSL_read() 返回&lt;= 0,请勿再次调用SSL_read()。立即调用SSL_get_error(),然后循环回到循环顶部的SSL_read()仅当错误不是致命错误(SSL_ERROR_WANT_READSSL_ERROR_WANT_WRITE 等)。

标签: c sockets openssl


【解决方案1】:

您的循环调用SSL_read() 的次数过多,并且当确实没有更多数据要读取时(SSL_ERROR_WANT_READ 条件),您的错误处理不会中断循环。尝试更多类似的方法:

char *sslRead (connection *c)
{
    const int readSize = 1024;
    char *rc = NULL;
    int received, count = 0;
    int TotalReceived = 0;
    fd_set fds;
    struct timeval timeout;
    char buffer[1024];

    if (c)
    {
        while (1)
        {
            received = SSL_read (c->sslHandle, buffer, readSize);
            if (received > 0)
            {
                TotalReceived += received;
                printf("Buffsize - %i - %.*s \n", received, received, buffer);
            }
            else
            {
                count++;

                //printf(" received equal to or less than 0\n")
                int err = SSL_get_error(c->sslHandle, received);
                switch (err)
                {
                    case SSL_ERROR_NONE:
                    {
                        // no real error, just try again...
                        printf("SSL_ERROR_NONE %i\n", count);
                        continue;
                    }   

                    case SSL_ERROR_ZERO_RETURN: 
                    {
                        // peer disconnected...
                        printf("SSL_ERROR_ZERO_RETURN %i\n", count);
                        break;
                    }   

                    case SSL_ERROR_WANT_READ: 
                    {
                        // no data available right now, wait a few seconds in case new data arrives...
                        printf("SSL_ERROR_WANT_READ %i\n", count);

                        int sock = SSL_get_rfd(c->sslHandle);
                        FD_ZERO(&fds);
                        FD_SET(sock, &fds);

                        timeout.tv_sec = 5;
                        timeou.tv_nsec = 0;

                        err = select(sock+1, &fds, NULL, NULL, &timeout);
                        if (err > 0)
                            continue; // more data to read...

                        if (err == 0) {
                            // timeout...
                        } else {
                            // error...
                        }

                        break;
                    }

                    case SSL_ERROR_WANT_WRITE: 
                    {
                        // socket not writable right now, wait a few seconds and try again...
                        printf("SSL_ERROR_WANT_WRITE %i\n", count);

                        int sock = SSL_get_wfd(c->sslHandle);
                        FD_ZERO(&fds);
                        FD_SET(sock, &fds);

                        timeout.tv_sec = 5;
                        timeou.tv_nsec = 0;

                        err = select(sock+1, NULL, &fds, NULL, &timeout);
                        if (err > 0)
                            continue; // can write more data now...

                        if (err == 0) {
                            // timeout...
                        } else {
                            // error...
                        }

                        break;
                    }

                    default:
                    {
                        printf("error %i:%i\n", received, err); 
                        break;
                    }
                }     

                break;
            }
        }
    }

    return rc;
}

【讨论】:

  • 谢谢雷米,这是正确的!再次感谢您用代码而不是一堆英语与我交谈,我很感激。
  • 嗨 Remy,经过一段时间的测试,它可以工作,但有一个问题 - 即使在此之前有数据可用,这也会等待 5 秒。因此,两次发送/接收数据将需要 20 秒,而在快速服务器上则需要 1-2 秒。有没有办法让它读取 IF 数据可用,并仅在没有数据可用时让它等待?我想保持 5 秒超时,因为有些服务器需要它,但大多数不需要。我只是喜欢它,这样它只会在必要时等待这段时间,而不是默认情况下。谢谢。
  • 显然,您必须调整 select() 超时以满足您的需要。但是您必须考虑网络延迟。如果消息之间没有明显的分隔符,您只需要处理超时。我给你的已经满足了你的要求。只要有数据可用,就不需要等待。它仅在数据不可用时等待。但是,究竟发送了什么不会以某种方式界定其消息?
  • 来自手册页:“SSL_pending(3) 可用于确定是否有可用于立即检索的缓冲字节。”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-07
  • 2010-10-31
  • 2013-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多