[tcp] 异步connect是否成功?
我原先的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是否收/发数据,那是另一回事了。”
相关文章
- 压测Cubieboard - 01 25, 2013
- 网络编程拾遗 - 04 16, 2010
- [tcp] http_load报"Cannot assign requested address" - 01 27, 2010
即便应用hang住,tcp层还是会建立这个连接,最好的判断是接受个数据
像连到ssh端口一样,会返回个banner
题外话,异步connect我一般也就select 了,有些老机器估计还没epoll
早向ssh学习就不用调试那么久了 :)
找到一个方法, 不过还没验证:
1,当本地还没调用connect函数,却将套接字送交epoll检测,epoll会产生一次 EPOLLPRI | EPOLLOUT | EPOLLERR, 也就是产生一个值为14的events.
2,当本地connect事件发生了,但建立连接失败,则epoll会产生一次 EPOLLIN | EPOLLPRI | EPOLLHUP, 也就是一个值为19的events.
3,当connect函数也调用了,而且连接也顺利建立了,则epoll会产生一次 EPOLLOUT, 值为4,即表明套接字已经可写。
如果getoptsock不为0,下一步怎么处理?继续等待,隔时再来检查?
有最终答案吗?
发现connect失败后该怎么做取决于应用。如果是ftp、curl一类的,连不上就退出,报个错给用户,用户自己决定下面怎么办;如果是监控程序,那么隔几秒再重试是更好的选择。
在网上看见一个根据内核代码的处理方式取巧的方法,不用设置sockfd为nonblocking模式。在调用connect(2)之前设置send buffer timeout(SNDTIMEO),然后connect(2)会在设置的时间范围内超时退出,返回EINPROGRESS,如果对方在挂掉的话。这样sendmsg也会在设置的范围内timeout。用blocking的模式使用起来比较简单。
我觉得还是避免使用技巧。何况使用blocking模式也不是适合所有系统。