BIO_do_handshake卡住的根源在于默认BIO_s_socket为阻塞模式,即使socket设为非阻塞,BIO_do_handshake返回-1时实际表示需重试,必须检查BIO_should_retry/BIO_should_read/BIO_should_write标志,并配合select/poll/epoll等待就绪后再调用,而非直接判定失败。

为什么直接用 BIO_do_handshake 会卡住?
因为默认的 BIO_s_socket 创建的 BIO 是阻塞模式,哪怕你把底层 socket 设为非阻塞,BIO_do_handshake 和 BIO_read/BIO_write 在握手或 I/O 未就绪时仍会直接返回 -1 并置 BIO_should_retry 标志,而不是等待。不检查这个标志就认为出错,通信必然失败。
关键不是“让 BIO 变非阻塞”,而是:每次调用后必须检查 BIO_should_retry()、BIO_should_read()、BIO_should_write(),再配合 select/poll/epoll 等机制决定何时重试。
-
BIO_should_retry(bio)是总开关,为 false 才真出错 -
BIO_should_read(bio)表示需要等 socket 可读(比如握手时缺 ServerHello) -
BIO_should_write(bio)表示需要等 socket 可写(比如发送 ClientHello 后需 flush)
如何创建并配置非阻塞 SSL BIO 对?
必须手动控制 socket 层和 SSL 层的耦合方式:先建非阻塞 socket,再套一层 BIO_s_socket,最后用 BIO_new_ssl 或手配 SSL_set_bio 绑定。不能依赖 BIO_new_connect 这类封装——它默认建阻塞 socket。
服务端典型流程:
int sock = socket(AF_INET, SOCK_STREAM, 0); int flags = fcntl(sock, F_GETFL, 0); fcntl(sock, F_SETFL, flags | O_NONBLOCK); BIO *sbio = BIO_new(BIO_s_socket()); BIO_set_fd(sbio, sock, BIO_NOCLOSE); // 不接管 close SSL_CTX *ctx = SSL_CTX_new(TLS_server_method()); SSL *ssl = SSL_new(ctx); SSL_set_bio(ssl, sbio, sbio); // rbio == wbio // 此时 ssl BIO 已绑定,但尚未握手
客户端类似,但需额外调用 SSL_set_connect_state(ssl)。
BIO_do_handshake 返回 -1 时该怎么处理?
这是最常踩坑的地方:看到 -1 就 log error + close,实际只是“现在做不了,稍后再试”。正确响应逻辑必须分三步走:
- 调用
BIO_do_handshake(ssl_bio) - 若返回 ≤ 0,立即查
SSL_get_error(ssl, ret)—— 注意不是ERR_get_error() - 若错误码是
SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE,则进入事件循环等待对应 socket 事件;其他错误(如SSL_ERROR_SSL)才是真失败
示例片段:
BIO_read
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











