基于F-Stack+DPDK的C++ EPOLL客户端SSL_connect阻塞求助
问题分析与解决方案
核心原因
F-Stack 是用户态网络栈,其 socket 并非系统标准套接字,直接使用 OpenSSL 原生的阻塞 SSL_connect 会导致 IO 逻辑不兼容,触发循环等待(对应你看到的 4097 回调标识),最终阻塞程序。同时未适配 F-Stack 的 IO 接口与 OpenSSL 绑定,会引发数据传输异常(乱码)。
具体解决步骤
1. 将 SSL 对象强制设为非阻塞模式
F-Stack 不支持系统级的 fcntl 非阻塞设置,需直接对 SSL 对象配置:
// OpenSSL 3.x 直接调用该接口设置非阻塞 SSL_set_nonblocking(ssl, 1); // 额外开启自动重试与分段写入模式,适配用户态网络栈 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY | SSL_MODE_ENABLE_PARTIAL_WRITE);
2. 用事件驱动替换阻塞的 SSL_connect
结合 F-Stack 的 EPOLL 机制,循环处理握手流程,不再依赖阻塞调用:
int ret; while (true) { ret = SSL_connect(ssl); if (ret == 1) { // TLS 握手成功,跳出循环 break; } int ssl_err = SSL_get_error(ssl, ret); if (ssl_err == SSL_ERROR_WANT_READ) { // 注册 EPOLLIN 事件,等待可读后继续握手 struct epoll_event event = {.events = EPOLLIN, .data.fd = ff_fd}; epoll_ctl(epfd, EPOLL_CTL_MOD, ff_fd, &event); } else if (ssl_err == SSL_ERROR_WANT_WRITE) { // 注册 EPOLLOUT 事件,等待可写后继续握手 struct epoll_event event = {.events = EPOLLOUT, .data.fd = ff_fd}; epoll_ctl(epfd, EPOLL_CTL_MOD, ff_fd, &event); } else { // 握手失败,处理错误 fprintf(stderr, "TLS 握手失败,错误码:%d\n", ssl_err); goto cleanup; } // 等待 EPOLL 事件触发 int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); if (nfds < 0) { perror("epoll_wait 失败"); goto cleanup; } }
3. 绑定 F-Stack 专属 IO 接口到 OpenSSL
OpenSSL 默认使用系统 read/write,必须替换为 F-Stack 的 ff_read/ff_write,通过自定义 BIO 实现:
// 自定义 F-Stack 读写回调 static int ff_ssl_read(BIO *bio, char *buf, int num) { int fd = BIO_get_fd(bio, NULL); return ff_read(fd, buf, num); } static int ff_ssl_write(BIO *bio, const char *buf, int num) { int fd = BIO_get_fd(bio, NULL); return ff_write(fd, buf, num); } // 创建并绑定自定义 BIO BIO_METHOD *ff_bio_method = BIO_meth_new(BIO_TYPE_SOCKET, "F-Stack Socket BIO"); BIO_meth_set_read(ff_bio_method, ff_ssl_read); BIO_meth_set_write(ff_bio_method, ff_ssl_write); BIO_meth_set_destroy(ff_bio_method, BIO_destroy_socket); BIO *bio = BIO_new(ff_bio_method); BIO_set_fd(bio, ff_fd, BIO_NOCLOSE); SSL_set_bio(ssl, bio, bio);
4. 排查版本兼容性问题
你手动升级了 OpenSSL 3.1.3,需确保编译与运行时的库路径一致:
- 编译时指定 OpenSSL 3.1.3 的链接参数:
-L/path/to/openssl-3.1.3/lib -lssl -lcrypto - 运行前设置环境变量:
export LD_LIBRARY_PATH=/path/to/openssl-3.1.3/lib:$LD_LIBRARY_PATH - 确认 F-Stack 编译时也链接了同一版本的 OpenSSL
5. 解决乱码问题
乱码是因为握手阶段的二进制 TLS 数据被误当明文打印,或缓冲区配置异常:
- 不要在 TLS 握手完成前打印 socket 接收的数据
- 通过
ff_setsockopt配置合适的收发缓冲区大小:
int buf_size = 16384; ff_setsockopt(ff_fd, SOL_SOCKET, SO_SNDBUF, &buf_size, sizeof(buf_size)); ff_setsockopt(ff_fd, SOL_SOCKET, SO_RCVBUF, &buf_size, sizeof(buf_size));
内容的提问来源于stack exchange,提问作者NeViXa
相关产品推荐
相关产品推荐

