可以将套接字从非阻塞更改为阻塞然后再次非阻塞吗?

Is it okay to change socket from non-blocking to blocking and then non-blocking again?

我的应用程序的整个逻辑都使用非阻塞套接字,但在连接阶段,我发现在使用 SSL_connect() 执行 SSL 握手之前,最好使套接字阻塞。这是因为否则它会在握手成功完成之前创建一个繁忙的循环,并且在此之前实际阻塞套接字应该更有效。

这是我的连接逻辑的伪代码:

bool connect(host)
{
    int socket;
    init_socket(socket);

    set (socket, NONBLOCKING);
    connect_with_timeout(socket, host, 2s);
    if (timeout_failed || connect_errors) return false;

    set (socket, BLOCKING);
    SSL_connect (socket);
    if (ssl_connect_errors) return false;

    set (socket, NONBLOCKING);
    return true;
}

非阻塞套接字上的 SSL 握手如下所示:

do
{
    SSL_connect(socket);
}
while (!SSL_connection_errors);

像这样更改套接字类型是否被认为是一种不好的做法?当你这样做时,在低层次上到底发生了什么?

我知道这似乎是性能上的微小改进,但我想做对,因为我的应用程序可能每 30 秒尝试重新连接一次,而用户偶尔会收到不到 1 秒的大 CPU秒杀。

编辑:我在这个问题上得到的答案让我明白尝试在非阻塞套接字上读取然后休眠并不是一个好主意。但是,我确实需要一个独立于平台的 poll 解决方案,所以我已经继续添加了一个 select,读取操作超时为 1s,测试了 Windows 和 Linux 和 CPU 现在使用率较低。谢谢。

看来这是一个XY问题。您真正的问题是您不了解如何进行非阻塞套接字操作。对于 OpenSSL,当非阻塞操作会阻塞时,您应该调用 SSL_get_error。如果错误是 SSL_ERROR_WANT_READ,您应该在套接字可读时重试该操作。如果错误是 SSL_ERROR_WANT_WRITE,您应该在套接字可写时重试该操作。

下一个问题是——如何等待套接字变为可读或可写?这取决于你的平台。对于 Linux,您可以使用 poll,它也需要超时,因此您可以控制等待的时间。

保持套接字非阻塞。呼叫SSL_connect。如果呼叫被阻止,请呼叫 SSL_get_error,您将得到 SSL_ERROR_WANT_READSSL_ERROR_WANT_WRITE。使用poll等待socket变为readable/writable。如果出现超时或错误,请适当处理。如果套接字变成readable/writable,再调用SSL_connect

您应该以同样的方式处理 SSL_readSSL_write