今天才发现,这里已经两年多没有被照看过了,估计连树都要长出来了吧。
在用 Windows 的 WSAEventSelect 模式进行网络编程时,比较固定的一个模式是这样的:
|
|
当要处理的 sock 属于一个 OpenSSL 连接时,只要把当中的 recv 换成 SSL_read 就行了,当然前面还得加上些 readWaitonWrite 之类的标志位检查啥的,这里就不详细列举了,请参考《Network Security with OpenSSL》一书中的 5.2.2.3 小节。
但我在实际使用中(事实上是在压力测试中)发现,当程序运行一段时间之后,WSAWaitForMultipleEvents 就会忽然不再返回可读信号了,从而导致对该 socket 的接收操作完全停止。
经过各种调试手段,甚至是在 OpenSSL 中加入调试输出之后,终于发现问题出在 SSL_read 的实现机制上,貌似 OpenSSL 实现了某种程度的读写缓冲(具体没细看),使得对 SSL_read 的一次调用,并不一定会触发其对底层 socket 的读取操作。而如果没有对底层 socket 的读取操作,那么 windows 的对应 event 对象就不会被 reenable (参考 MSDN 中对 WSAEventSelect 接口的说明文档),从而导致 WSAWaitForMultipleEvents 不再对该事件作检查。
原因找到了,那么相应的解决方法也就不难发现了,对于我的使用场景来说(不完全是像上面的示例片断那么清晰),最简单的作法就是在 SSL_read 之前加一个空的 recv 调用,其传给 recv 的第二、三个参数的值分别是 NULL 和 0 ,这样就能强制触发 windows event 的 reenable ,同时又不会影响到 SSL 对象内部的读取状态了。
PS. 遇到这个问题时,最重要的一步其实是如何能够稳定而快速的复现问题。一开始做压力测试时,只有在连续跑个一天以上时才会不定时的出现,导致效率很低;后来偶然发现,通过虚拟机搭建的受限环境,反而能很快复现问题。想来这也是另一种形式的压力测试吧,正常的压力测试是保持环境不变,加大请求压力;这里则变成了保持请求不变,同时压缩可用环境。