在我的ActiveUpdate模块(从服务器下载文件)中,如果服务器丢弃FIN包,则关闭OpenSSL连接将挂起(等待超过2小时)。
SSL连接的伪代码关闭
def ssl_close():
if (!SSL_shutdown(m_ssl)):
…
shutdown(SSL_get_fd(m_ssl), SHUT_WR);
SSL_shutdown(m_ssl)
...
挂起连接的netstat输出
tcp 0 0 192.168.133.135:52453 10.203.136.169:4122 FIN_WAIT2 off (0.00/0/0)
https://www.openssl.org/docs/ssl/SSL_shutdown.html
为了完成双向关闭握手,必须再次调用SSL_shutdown()。 如果底层BIO阻塞,则SSL_shutdown()仅在握手步骤完成或发生错误后才会返回。
如果调用SSL_shutdown()一次,它就不会挂起,是否对内存和连接资源有任何影响?
是否有超时机制来控制OpenSSL中的SSL_shutdown()或更好的方法?
答案 0 :(得分:2)
第一个SSL_shutdown将导致向对等方发送关闭警报,以通知对等方不再有SSL数据 - 类似于在普通套接字上发送FIN或关闭SHUT_WR。第二个SSL_shutdown将一直等到收到来自对等方的关闭警报。
要确保套接字完全处于活动状态以便对等方发送SSL关闭警报并且您将收到它,我建议只在第二次SSL_shutdown完成后关闭底层套接字。即首先将套接字从SSL完全降级为plain,然后关闭普通套接字。
答案 1 :(得分:1)
...如果服务器丢弃FIN包。
我不相信你看到的问题是由于丢失了FIN
。我怀疑(作为Steffen)您正在等待客户端的close_notify
表单,但客户端没有发送它。
if(!SSL_shutdown(m_ssl))...
SSL_shutdown
返回-1,0和1;不是0和1。
如果调用SSL_shutdown()一次,它就不会挂起,是否对内存和连接资源有任何影响?
我不相信,但在Valgrind或其他检漏仪工具下验证可能会更好。
是否有超时机制来控制OpenSSL中的SSL_shutdown()或更好的方法?
我不相信有超时。
您可以使用两种策略:
close_notify
调用SSL_shutdown
一次是策略1.只需关闭套接字即可完成。
两次调用SSL_shutdown
是策略2.问题是,并非所有客户端都发送它。很多时候,他们只是在阅读了他们要求的所有内容之后才关闭套接字。
您可以通过捕获SIGPIPE
来检测第二种情况。如果连接上有SIGPIPE
,则客户端关闭套接字。只需拨打SSL_shutdown
一次。
Eric Rescorla在An Introduction to OpenSSL Programming (Part I)更详细地介绍了它。这是他提供的服务器端代码:
51 r=SSL_shutdown(ssl);
52 if(!r){
53 /* If we called SSL_shutdown() first then
54 we always get return value of ’0’. In
55 this case, try again, but first send a
56 TCP FIN to trigger the other side’s
57 close_notify */
58 shutdown(s,1);
59 r=SSL_shutdown(ssl);
60 }
61
62 switch(r){
63 case 1:
64 break; /* Success */
65 case 0:
66 case -1:
67 default:
68 berr_exit("Shutdown failed");
69 }