mongdb数据库

mongodb使用总结

标签


CLOSE调用导致TCPRST问题

2015年02月02日

用正确的工具,做正确的事情

之前为了解决客户端过缓存平台后会导致获取跨域文件失败的问题,个人临时写了一个TCP服务器放到缓存服务器上以处理客户端跨域文件请求,github 项目地址。因为跨域文件请求的TCP端口号固定为843端口,因此服务器端对跨域请求处理逻辑:

accept用户连接,创建子进程处理用户请求:不读取/不处理客户端发送的请求数据,直接通过TCP连接发送跨域文件给客户端,然后close该TCP连接。

用上面简单粗暴的方式实现后直接部署到线上缓存服务器,浏览器测试相关视频播放可以正常了,说明客户端跨域文件访问失败导致视频播放异常的问题解决了。 然后总觉得上面的实现方式有点太挫(呵呵,隐隐有一种不祥的预感,始终认为一个开发人员良好的第六感对所开发系统的稳定性起着关键的作用),虽然只是临时性解决方案,孙然只有短短几行代码,第六感告诉有必要再进一步观察下该TCP服务器的运行逻辑是否完全符合预期,因此抓包看下TCP交互数据流是否正常,果然发现了问题,服务器端调用close直接触发了服务器端发出了rst数据包:

服务器端close系统调用直接出发rst数据包

如果客户端不主动断开连接,一直在等待接收服务器端数据比如receive(),服务器端的rst数据包会导致receive系统调用返回-1,如果客户端对系统调用反馈失败的处理方式是关闭客户端的话,问题就大了,孙然实际用常用的浏览器测试并没法发生这种问题,但是作为一个有节操的技术人员(孙然好久不写代码)也不能放过这种问题,及时没必要解决也要搞清楚问题所在,这完全跟预期不符:

服务器端调用close的预期是服务器端主动关闭连接,温柔的触发tcp 四次握手断开连接,而非粗暴的rst。

谷歌搜索相关关键字,得到一个权威的解释是,相关文档

TCP连接的一端,如果调用close()时其接收缓冲区有未读取的数据,此时不会触发TCP四次握手断开连接,而是直接触发一个RST数据包。

TCP协议为什么采用这种的设计和实现,进一步思考:

从TCP层看,当连接的某一端其接收缓存区中存在数据没有被应用层接收,此时应用层通过系统调用通知TCP层关闭连接,这从TCP层看可以认为应用层“读并处理”数据发生异常,此时上层调用close()关闭连接,不应该被认为是一种正常关闭连接的行为,并且这种行为也应该被TCP连接的另外一端感知到,因此此时TCP层直接触发一个RST数据包,而非标准的四次握手断开连接。

enjoy the life !!!