使用mbedtls库时,如何结合select()检测SSL上下文缓存数据?
解决mbedtls结合select()时的缓存数据读取问题
要解决这个问题,核心是先检查mbedtls SSL上下文的内部缓存,再用select()监听底层套接字——因为SSL层会提前将底层套接字的数据读取并缓存,此时底层fd无数据但SSL上下文有未读内容,select()不会触发。
关键思路
- 每次尝试读取前,先调用
mbedtls_ssl_get_bytes_avail()检查SSL上下文缓存中是否有未读字节:- 返回值>0:直接调用
mbedtls_ssl_read()读取缓存数据,无需等待select() - 返回值=0:再用select()监听底层套接字的可读事件
- 返回值>0:直接调用
- 处理
mbedtls_ssl_read()返回的MBEDTLS_ERR_SSL_WANT_READ/MBEDTLS_ERR_SSL_WANT_WRITE错误:- 这类错误表示当前SSL操作需要等待底层套接字的可读/可写事件,需重新进入select()等待
示例代码
#include <mbedtls/ssl.h> #include <mbedtls/net.h> #include <sys/select.h> int ssl_select_and_read(mbedtls_ssl_context *ssl, mbedtls_net_context *net, void *buf, size_t len) { int ret; fd_set read_fds, write_fds; struct timeval tv; // 先检查SSL上下文缓存是否有未读数据 size_t avail = mbedtls_ssl_get_bytes_avail(ssl); if (avail > 0) { return mbedtls_ssl_read(ssl, buf, len); } // 缓存无数据,用select等待底层套接字可读 FD_ZERO(&read_fds); FD_SET(net->fd, &read_fds); tv.tv_sec = 5; // 自定义超时时间 tv.tv_usec = 0; ret = select(net->fd + 1, &read_fds, NULL, NULL, &tv); if (ret < 0) { // select错误处理 return ret; } else if (ret == 0) { // 超时处理 return -1; } // 套接字可读,调用ssl_read ret = mbedtls_ssl_read(ssl, buf, len); while (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) { if (ret == MBEDTLS_ERR_SSL_WANT_READ) { FD_ZERO(&read_fds); FD_SET(net->fd, &read_fds); ret = select(net->fd + 1, &read_fds, NULL, NULL, &tv); } else { // MBEDTLS_ERR_SSL_WANT_WRITE FD_ZERO(&write_fds); FD_SET(net->fd, &write_fds); ret = select(net->fd + 1, NULL, &write_fds, NULL, &tv); } if (ret <= 0) { return ret; } ret = mbedtls_ssl_read(ssl, buf, len); } return ret; }
注意事项
mbedtls_ssl_get_bytes_avail()仅能获取SSL应用层缓存的未读数据,不包括正在处理的SSL记录- 处理
MBEDTLS_ERR_SSL_WANT_WRITE时,需要监听套接字的可写事件,因为SSL握手或加密操作可能需要写入数据 - 实际使用中需根据业务需求调整超时时间和错误处理逻辑
内容的提问来源于stack exchange,提问作者n3mo
相关产品推荐
相关产品推荐

