OpenSSL解惑3:SSL_read的阻塞超时与它是否等同于recv函数

本文介绍了在使用SSL_read时遇到的程序卡死问题,指出该问题源于socket的阻塞,并提供了设置socket超时参数的解决方案。通过设置SO_RCVTIMEO选项,可以避免长时间等待数据导致的阻塞。同时,提醒读者注意不同操作系统中超时单位的差异,并建议在遇到SSL_read返回负值时,考虑错误处理,特别是SSL_ERROR_WANT_READ情况,可能需要重新接收数据。文章强调了理解SSL_read完整错误处理机制的重要性,并分享了作者的观点,不推荐直接使用openssl的某些特定错误代码来判断错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

初学者经过前面的几篇文章后很容易就会在使用中发现一个严重的问题:我们在调用 SSL_read 函数的时候会导致程序卡死一动不动。我在网上看到过很多的网友发帖子讨论,写了一版又一版的代码,各种 ssl 函数调用了个遍 ... 其实这个问题只看 openssl 的各种 api 文档是解决不了的。其实解决办法也很简单,其实只要说原理大家就明白了:那就是 SSL_read 的阻塞其实是 socket 造成的,原因嘛,我们前面不是说过了 ssl/tls 不过是封装了加密的 socket 吗?而这种封装都集中在连接过程中,数据通讯过程完全就是 socket 过程,只是数据是加密过的而已。所以 SSL_read 的阻塞解决起来是很简单的,只要设置 socket 超时参数就可以了,网友说 openssl 文档中说可以用 select 函数 ... 严格来说这个做法不全对,因为 socket 设置超时的做法是很多的,有兴趣的大家可以看看我们前面的电子邮件系列文章。下图就是用前面的例子连接一个超时的情况,可以看到连接后就不动弹了:

无超时设置时

而有超时设置的情况下 SSL_read 会返回 -1,这和 socket 函数的原始 recv 函数返回值是一样的结果。熟悉 socket 编程的网友应该对这个返回值应该很是熟悉了。

有超时设置时

我使用的超时设置函数如下:

void SetTimeout_recv(SOCKET so, int sec){ struct timeval timeout; // : TTimeVal; timeout.tv_sec = sec; //秒//windows 下就是 毫秒 timeout.tv_usec = 0; //毫秒,不对是 微秒 _setsockopt(so, SOL_SOCKET,SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));}//

这里要特别注意的是,代码是 linux 风格的,虽然在 windows 下也能正常运行,但是运行的结果相同的参数在 linux 下超时单位是秒,在 windows 下超时的单位则是毫秒。

但是,这样仍然是有问题的。实际上 SSL_read 的结果并不完全等同于 recv 的结果,虽然我们在大多数情况下这样使用。不过我们仍然应该了解 SSL_read 完整的错误处理方式。实际上,openssl 定义了一个 SSL_get_error 来判断 SSL_read 函数的运行结果,使用的方法类似如下:

err = SSL_read(ssl, buf, sizeof(buf) - 1); sslerr = SSL_get_error(ssl, err);

如果是返回值是 SSL_ERROR_WANT_READ,则要重新接收数据。这一定义是在 openssl 中的,具体位置见下图:

SSL_ERROR_WANT_READ在openssl中的定义

在图中可以看到还有一些类似的参数,虽然有网友写了使用这些参数的封装,不过我个人并不赞成在编程中使用这些参数。因为这样明显违背了 ssl 的设计初衷。我推荐大家这样使用:设定一个超时值,当无论是 recv 函数还是 SSL_read 返回小于0的数值时就认为是错误了。当然了我们要明白,在极端的情况下 SSL_read 返回 -1 并不一定是发生了错误,有可能只是没有收到完整的可解密包而已。什么叫作"完整的可解密包"?这个就要以后我们讲解了具体的加解密算法后才好说了。目前来说我们只要知道,字节流加密后对它的解密处理就不能单纯的按字节流来处理了,因为有些(应该是大部分)算法比如 des 会加入补充的字节。

实际上 openssl ,不,应该是 ssl/tls 中还会对数据进行分块,不过就算协议不分块,光是加解密的算法限制就会造成以下的情况:socket 收到了一个字节,但对应的 SSL_read 并不能返回数字 1 表示收到了一个数据,还是只能返回小于 1 的数值,因为这一个字节是不能独立进行解密的。这在我们设置超时的话问题不大,因为一定时间内还不能返回一个数据块的情况只有网络很差的情况下才会出现,就认为是出错了也未尝不可,但非阻塞 socket 模式下的处理就要小心多测试才行。

这几篇 ssl 的文章阅读量不高,不过我觉得还是很重要的,所以还是要坚持写完整。谢谢大家。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值