非阻塞套接字下SSL_read与SSL_write交错调用的代码正确性问询
问题背景
在使用SSL与非阻塞套接字时,SSL_read和SSL_write函数可能因需要对套接字执行读或写操作而需重试,对应返回SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE。此类场景下,需根据ssl_get_error的结果调整poll事件,重复调用相同参数的SSL函数直至完成,再进行下一次调用。
但在无请求也会产生输出数据的场景(如推送请求)中,需在同一线程内交错调用SSL_read与SSL_write。为此编写了一段简化代码如下:
poll_set = read+interrupt // interrupt is set if write data is available read_requires_read = false read_requires_write = false write_requires_read = false write_requires_write = false next_read_requires_read = false next_read_requires_write = false next_write_requires_read = false next_write_requires_write = false while poll(poll_set) if poll_was_read: if read_requires_read: SSL_read(...) //set next_read_requires_* based on ssl_get_error if write_requires_read: SSL_write(...) //set next_write_requires_* based on ssl_get_error if not read_requires_read and not read_requires_write: //no pending read, read next SSL_read(...) //set next_read_requires_* based on ssl_get_error if poll_was_write: if read_requires_write: SSL_read(...) //set next_read_requires_* based on ssl_get_error if write_requires_write: SSL_write(...) //set next_write_requires_* based on ssl_get_error if poll_was_interrupt: if not write_requires_read and not write_requires_write: //no pending write SSL_write(...) //set next_write_requires_* based on ssl_get_error read_requires_read = next_read_requires_read // read_requires_write = next_read_requires_write write_requires_read = next_write_requires_read write_requires_read = next_write_requires_write poll_set = {interrupt} if read_requires_read or write_requires_read: poll_set.add(read) if read_requires_write or write_requires_write: poll_set.add(write) if poll_set = {interrupt}: poll_set = {read, interrupt} //if all calls completed, reset to initial
疑问:不确定这段代码是否正确,因为它允许在之前的SSL_read返回SSL_ERROR_WANT_READ或SSL_ERROR_WANT_WRITE时,仍调用SSL_write,特此问询该实现的正确性。
正确性分析
你的实现整体方向是对的,在非阻塞SSL上下文下,交错调用SSL_read和SSL_write是完全允许的,具体分析如下:
SSL连接的双向状态独立性
SSL/TLS协议的读、写操作内部状态是分离的,两者互不干扰。即使SSL_read处于等待读/写的挂起状态,只要SSL_write的内部状态允许(比如没有未完成的写操作依赖,或当前套接字写事件就绪),调用SSL_write是合法的,不会破坏SSL_read的后续重试逻辑。代码逻辑的合理性
你通过read_requires_*和write_requires_*两组变量分别跟踪读、写操作的待处理状态,仅在对应poll事件就绪时才重试未完成的SSL操作,这完全符合非阻塞SSL的处理规范。
当有新的写数据触发interrupt时,仅在没有未完成的写操作时才发起新的SSL_write,避免了重复发起未完成的写请求,逻辑严谨。
每次事件处理后重新计算poll_set,确保只监听当前必需的事件,避免不必要的资源消耗,效率合理。需要修正的笔误
代码中存在一处明显错误:write_requires_read = next_write_requires_write应该改为write_requires_write = next_write_requires_write,否则会导致写操作的状态跟踪混乱,必须修正。状态跟踪的注意事项
每次调用SSL函数后,必须严格根据ssl_get_error的返回值更新对应的next_*状态:- 若
SSL_read返回SSL_ERROR_WANT_READ,则设置next_read_requires_read = true,其余读相关状态设为false; - 若返回
SSL_ERROR_WANT_WRITE,则设置next_read_requires_write = true,其余读相关状态设为false; - 若操作成功或出现致命错误,需重置对应操作的状态变量。
- 若
结论
只要修正上述笔误,并确保状态跟踪逻辑准确,你的实现是正确的。非阻塞SSL场景下,允许在未完成的读操作存在时发起写操作,两者的状态管理相互独立,不会产生冲突。
内容的提问来源于stack exchange,提问作者Domso

