[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是否收/发数据,那是另一回事了。”

相关文章

分类

7 Comments

hoterran said:

即便应用hang住,tcp层还是会建立这个连接,最好的判断是接受个数据
像连到ssh端口一样,会返回个banner

题外话,异步connect我一般也就select 了,有些老机器估计还没epoll

DongHao Author Profile Page said:

早向ssh学习就不用调试那么久了 :)

ideawu said:

找到一个方法, 不过还没验证:
1,当本地还没调用connect函数,却将套接字送交epoll检测,epoll会产生一次 EPOLLPRI | EPOLLOUT | EPOLLERR, 也就是产生一个值为14的events.
2,当本地connect事件发生了,但建立连接失败,则epoll会产生一次 EPOLLIN | EPOLLPRI | EPOLLHUP, 也就是一个值为19的events.
3,当connect函数也调用了,而且连接也顺利建立了,则epoll会产生一次 EPOLLOUT, 值为4,即表明套接字已经可写。

samuelo said:

如果getoptsock不为0,下一步怎么处理?继续等待,隔时再来检查?
有最终答案吗?

DongHao Author Profile Page said:

发现connect失败后该怎么做取决于应用。如果是ftp、curl一类的,连不上就退出,报个错给用户,用户自己决定下面怎么办;如果是监控程序,那么隔几秒再重试是更好的选择。

liuyuan said:

在网上看见一个根据内核代码的处理方式取巧的方法,不用设置sockfd为nonblocking模式。在调用connect(2)之前设置send buffer timeout(SNDTIMEO),然后connect(2)会在设置的时间范围内超时退出,返回EINPROGRESS,如果对方在挂掉的话。这样sendmsg也会在设置的范围内timeout。用blocking的模式使用起来比较简单。

DongHao Author Profile Page said:

我觉得还是避免使用技巧。何况使用blocking模式也不是适合所有系统。

留言:

关于文章

This page contains a single entry by DongHao published on 05 11, 2010 3:28 PM.

大项目不等于大trunk was the previous entry in this blog.

[kernel] epoll里的EPOLLET标记 is the next entry in this blog.

Find recent content on the main index or look in the 存档 to find all content.