与OpenSSL的多线程通信



我正在使用OpenSSL与服务器通信。我可以随时向服务器发送数据,服务器可能会也可能不会发回响应。服务器也可以在没有请求的情况下向客户端发送数据。

我在用BIO_new_SSL_connect制作的BIO上使用SSL,然后使用SSL_read和SSL_write。

我的第一种方法是使用阻塞套接字。我会启动一个线程,并在循环中调用SSL_read。每次调用都会阻塞,并且仅在读取某些数据时返回。每次通话后,我都可以打包数据并将其发送到某个地方。当我必须写的时候,我只是从另一个线程调用SSL_write。

我不知道在不同线程的同一连接上执行SSL_read时调用SSL_write是否有效。当我尝试断开(SSL_free/BIO_free)连接时,SSL_read调用崩溃。

这些来自不同线程的调用是否可取?如果没有,有没有更好的方法来解决这个问题(这似乎是一件很常见的事情)?

非阻塞插座可能工作得更好吗?

编辑:对不起,我应该补充一下,我已经实现了OpenSSL文档中描述的线程安全锁定。

OpenSSL库可以是线程安全的,但您必须自己提供锁定原语。来自OpenSSL常见问题解答:

多线程应用程序必须通过调用CRYPTO_set_locking_callback()CRYPTO_set_id_callback()为OpenSSL提供两个回调函数,适用于0.9.8[abc…]及以下的所有版本的OpenSSL。从1.0.0版本起,CRYPTO_THREADID_set_callback()和朋友们不赞成使用CRYPTO_set_id_callback()和相关API。threads(3)手册页对此进行了说明。

关于在SSL_read()中阻塞另一个线程时调用SSL_free(),请不要这样做。库是否是线程安全的无关紧要,这是违反API的。从单独的线程同时执行SSL_read()SSL_write()是可以的。如果另一个线程仍在使用SSL_CTX *,那么这些线程将需要相互协作,以确定何时可以安全地调用SSL_free(),因为允许另一个螺纹从释放的内存中读取或写入是错误的。毕竟,OpenSSL是一个库,而SSL_CTX *只是从堆中分配的一个普通结构。

您通常通过使用引用计数来跟踪何时可以安全地真正释放对象。如果您不想在应用程序代码本身中管理引用计数,则引用计数可以隐藏在自定义BIO中。

您提到了使用非阻塞模式的可能性。这本身不足以解决内存管理错误。您仍然需要引用计数来决定调用SSL_free()是否安全。

顺便说一句,如果你决定使用非阻塞模式,你可能应该将非阻塞模式与多线程相结合,以最大限度地提高多核系统的CPU利用率。但是,非阻塞OpenSSL实际上比常规的非阻塞BSD套接字复杂一个数量级。这是因为除了读取或写入时的常规"将阻止"之外,OpenSSL读取可能会报告需要写入操作才能完成,而OpenSSL写入可能会报告它需要读取操作才能完成。因此,您的非阻塞代码需要记住它在处理来自多路分解器的完成通知(例如,选择或轮询)后尝试了什么操作。此外,OpenSSL规定您必须传入与返回"将阻止"通知时尝试的参数完全相同的参数。因此,举个例子,您可能实际想要发送的任何新数据都必须进行缓冲,直到当前的OpenSSL写入完成。

最新更新