我原先的client端代码流程如下:
创建一个socket
设为异步socket(fcntl)
将socket加入epoll
connect到远端(此时connect调用返回非0,但errno为EINPROGRESS,表示正在建立连接中)
epoll_wait之
捕获到EPOLLOUT事件,此时便认为connect已经成功,client端开始发消息
这个过程通常能够运转,但是线上环境复杂多变,如果发生这种情况:server进程调用listen开始侦听后,被gdb或信号挂住了,此时异步connect会怎样?很遗憾,client端的epoll_wait依然返回EPOLLOUT,甚至往此socket里发消息都返回成功,只有当发的消息多得占完了server端的tcp缓冲以后(窗口收缩到很小),send调用才开始失败。这时候用 losf -i 看网络连接也很有趣,client端的机器显示连接建立了,server端的却显示没有这个连接。
仔细想想,OS这样做是正确的,毕竟connect的语义只是“连接”,当server挂住时,连接还是能成功的,但你能不能往里面发消息那就是另外一回事了。
所以对于应用来说,异步socket想要知道connect后连接是不是可以正常收发数据了,还是要靠应用层的一问一答才能知道。
====== 2010.5.14 ======
昨天同事朱照远给了一个更正确的解决方案,可参考之:
“收到EPOLLOUT也不能认为是TCP层次上connect(2)已经成功,要调用getsockopt看SOL_SOCKET的SO_ERROR是否为0。若为0,才表明真正的TCP层次上connect成功。至于应用层次的server是否收/发数据,那是另一回事了。”