【问题标题】:how to tell when no more progress can be made on an SSL connection如何判断 SSL 连接何时无法取得更多进展
【发布时间】:2015-02-27 00:28:19
【问题描述】:

这里

http://marc.info/?l=openssl-users&m=124386218929227

说明

"...这就是为什么理解任何端口上的任何可能的转发进度(以及返回 WANT_READ 的写入操作可能已经取得转发进度!)要求您重试所有端口上的所有挂起操作非常重要的原因。 ……”

那么我是否正确理解返回 WANT_READSSL_read() 可能已经取得了进展(即使它没有返回任何数据)?

我有一个事件驱动的单线程应用程序,它有 3 个非阻塞 ssl 套接字。当每个套接字完成其连接时,我会读取这些套接字,直到获得WANT_READ。我的理解是WANT_READ 意味着我现在可以调用select() 并等待套接字准备好再次读取。

select() 返回时,一个循环会遍历三个套接字,分别调用ssl_read()

说对 Socket 1 的读取返回了 WANT_READ 并且没有数据。 套接字 2 是否有可能返回一些数据,当没有更多要读取的内容时返回 WANT_READ,并且现在已经取得了一些进展,因此套接字 1 上的读取现在可以返回数据?但是由于循环已经完成了对套接字 1 的读取,所以它不会发生。

由于循环已通过 3 个套接字,它在那里等待并挂起。这种情况会发生吗?

如果是这种情况,我如何查看是否在所有 3 个插槽上都无法取得更多进展? 例如,假设循环贯穿;

  • socket 1 返回WANT_READ
  • socket 2 返回一些数据,然后WANT_READ(并取得进展,socket 1 现在可以返回数据)
  • socket 3 返回WANT_READ

但是根据上面的引用(任何向前的进展都需要重试所有挂起的操作),我应该再次重试所有套接字;所以第二次运行循环;

  • socket 1 现在返回数据,然后WANT_READ
  • socket 2 返回WANT_READ
  • socket 3 现在返回数据(因为 socket 1 上的读取取得了足够的进展,socket 3 返回数据),然后WANT_READ

但是如果套接字 3 上的最后一次读取取得了进展,那么套接字 2 现在再次返回数据怎么办?所以我的问题是(如果我的理解是正确的),我如何判断是否无法取得更多进展?

编辑 1:

所以我看到的是这样的:

我的循环遍历连接到 client1 的所有套接字(例如,有 2 个套接字)

  • socket 1:ssl_read() 返回 WANT_READ
  • socket 2:ssl_read()返回数据,ssl_read()再次返回数据,然后ssl_read()最终返回WANT_READ

然后,由于所有套接字都返回 WANT_READ,我等待select()。但是,我的应用仍在等待客户端已经发送的数据。如果我让客户端启动另一个连接(同时保持原件处于活动状态),则选择返回,这就是我所看到的:

  • socket 1:ssl_read() 返回数据,然后ssl_read() 再次返回 WANT_READ
  • socket 2:ssl_read() 返回 WANT_READ
  • socket 3(新):ssl_read() 返回数据,然后返回 WANT_READ。

所以 select 检测到新连接并运行我的循环,该循环遍历所有活动连接。这一次,除了套接字 3 上的新数据之外,它还从上次的套接字 1 中找到数据。所以我的理论是 select() 不会第一次返回,因为我在套接字上等待接收的数据1 已经到了,准备好等待我调用 ssl_read()。但上次我调用 ssl_read 时,我得到了 WANT_READ。

【问题讨论】:

    标签: openssl nonblocking


    【解决方案1】:

    SSL_read()返回负值时,应从SSL_get_error()获取错误码。如果错误代码是SSL_ERROR_WANT_READ,则意味着您应该等待(例如,使用select()poll)该套接字可读(直到一些数据从网络到达)。发生这种情况时,您应该再次在该套接字上重试 SSL_read() 您不必在您拥有的所有 SSL 套接字上重试 SSL_read(),只需在返回 SSL_ERROR_WANT_READ 的那些套接字上重试。您应该使用 SSL_write() 做类似的事情(当它返回 SSL_ERROR_WANT_WRITE 时)。

    但请注意,SSL_read() 可能会返回(SSL_get_error()SSL_ERROR_WANT_WRITESSL_write() 可能会返回 SSL_ERROR_WANT_READ。这可能会发生,因为 SSL 需要发送/接收除了您的数据之外的一些额外消息(例如,为了发送您的数据,它需要先发送一些消息并接收响应)。

    总结一下:

    1. n = SSL_read() 返回时:

      a)n > 0 --> 你刚刚收到n 字节的数据

      b)n < 0 --> err = SSL_get_error() 返回

      • SSL_ERROR_WANT_READ --> select() 直到套接字可读并再次调用SSL_read()
      • SSL_ERROR_WANT_WRITE --> select() 直到套接字可写并再次调用 SSL_read()

      c)n = 0 --> if(SSL_get_shutdown(SSL*) & SSL_RECEIVED_SHUTDOWN) : 对方做了干净的关机,否则关机不干净,但是SSL连接关闭了你可以关闭套接字(SSL*对象也是)

    2. n = SSL_write() 返回时:

      a)n > 0 --> 你刚刚发送了n字节的数据

      b)n < 0 --> err = SSL_get_error() 返回

      • SSL_ERROR_WANT_READ --> select() 直到套接字可读并再次调用 SSL_write()
      • SSL_ERROR_WANT_WRITE --> select() 直到套接字可写并再次调用 SSL_write

      c)n = 0 --> if((err = SSL_get_error(SSL*,err)) == SSL_ERROR_ZERO_RETURN) : 对方确实干净关闭,否则关闭不干净,但是SSL连接关闭你可以关闭套接字(SSL*对象也是)

    希望对你有帮助。

    【讨论】:

    • hhmm,这也是我的理解。我已将我看到的内容添加到问题的底部
    • 为什么要等到所有套接字都返回 SSL_ERROR_WANT_READ ?这没有任何意义。您可以在一次选择/轮询调用中等待不同的原因(对于每个套接字),然后根据选择调用的结果(对所有套接字进行一次调用),您可以分别为每个套接字执行适当的操作
    • 我检查所有套接字的唯一原因是邮件列表中的引用;当一个套接字取得进展时,应重试所有挂起的操作。这没有任何意义,但这就是 openssl 根据邮件列表帖子的工作方式。抛开这个问题不谈,你知道为什么当有数据可供读取时 select 会挂起吗?
    • 嗨,您实际上是正确的,事实证明我没有在 select 中包含所有套接字(doh!)。我已经测试了是否需要重试所有套接字,并且看起来这不需要像您正确描述的那样发生。谢谢! ps 解决了这个问题,我是否将您的答案标记为答案,即使严格来说这不是问题的原因?
    猜你喜欢
    • 2011-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-24
    • 1970-01-01
    • 2011-10-23
    • 2011-05-05
    • 1970-01-01
    相关资源
    最近更新 更多