版权声明:本文由胡文斌原创文章,转载请注明出处: 
文章原文链接:https://www.qcloud.com/community/article/102

来源:腾云阁 https://www.qcloud.com/community

Tcp关闭连接问题及注意

最近一段时间一直在学习阅读mina和nio的源码,也发现了一些问题无法解决,然后重读了一下tcp协议,收获颇多。(这就是带着问题去读书的好处)

这次就和大家分享一下我们的netframework服务总会抛出一个“connet reset by peer”的原因吧。通过抓包工具分析,主动关闭方直接发送了一个RST flags,而非FIN。就终止连接了。如下图所示:

为什么调用sokcet的close时只通过一次握手就终结连接了?

要分析这个原因那就得从关闭连接程的四次握手,有时也会是三次握手,说起。如下图所示:

大家都知道tcp正常的关闭连接要经过四次握手。如下所示:

在这四次握手状态中,有一个特别要注意的状态TIME_WAIT。这个状态是主动关闭方在收到被关闭方的FIN后会处于并长期(2个MSL时间,根据具体的实现不同,这个值会不同,在RFC 1122建议MSL=2分钟,但在Berkeley的实现上使用的值为30s,具体可以看www.rfc.net ,要是没有耐心去看英文的可以看这个网站www.cnpaf.net 里面有协议说明以及相应的源码,java源码中我没有发现这个值,我只能追踪到PlainSocketImpl.java这个类,再往下就是本地接口调用了,因此它是依赖本地操作系统的实现)处于的一个状态。也就是大约1-4分钟,然后由操作系统自动回收并将TCP连接设为CLOSED初始状态。如下图所示:

然而在socket的处于TIME_WAIT状态之后到它结束之前,该socket所占用的本地端口号将一直无法释放,因此服务在高并发高负载下运行一段时间后,就常常会出现做为客户端的程序无法向服务端建立新的socket连接的情况,过了1~4分钟之后,客户又可以连接上了,没多久又连接不上,再等1~4分钟之后又可以连接上,(上一个星期我们在做一个服务切换时遇到了这种情况)

这是因为服务方socket资源已经耗尽。netstat命令查看系统将会发现机器上存在大量处于TIME_WAIT状态的socket连接,我这边曾经出现达到了2w多个,并且占用大量的本地端口号。而此时机器上的可用本地端口号被占完,旧的大量处于TIME_WAIT状态的socket尚未被系统回收时,就会出现无法向服务端创建新的socket连接的情况。只能过2分钟之后等系统回收这些socket和端口资源之后才能服务,就这样往复下去。

TCP为什么要这么要让这种TIME_WAIT状态存活这么久呢?其原因有两个(参考stevens的unix网络编程卷1 第38页):

  1. 可靠地实现TCP全双工连接的终止。(确保最后的ACK能让被关闭方接收)
  2. 允许老的重复分节在网络中消逝。(TCP中是可靠的服务,当数据包丢失会重传,当有数据包迷路的情况下,如果不等待2MSL时,当客户端以同样地方式重新和服务建立连接后,上一次迷路的数据包这时可能会到达服务,这时会造成旧包被重新读取)

解决方法:

1、(推荐方法,只能治标不治本)重用本地端口设置SO_REUSEADDR和SO_REUSEPORT(stevens的unix网络编程卷1 第179~182页)有详情的讲解,这样就可以允许同一端口上启动同一服务器的多个实例。怎样理解呢?说白了就是即使socket断了,重新调用前面的socket函数不会再去占用新的一个,而是始终就是一个端口,这样防止socket始终连接不上,会不断地换新端口。Java 中通过调用Socket的setReuseAddress,详细可以查看java.net.Socket源码。【这个地方会有风险,具体可以看(stevens的unix网络编程卷1 第181页)】

2、修改内核TIME_WAIT等待的值,如果客户端和服务器都在同个路由器下,这个是非常推荐的。(链路好,重传机率低)

3、(不推崇,但目前我们是这样做的,这个是造成(“connet reset by peer”)的元凶)设置SO_LINGER的值,java中是调用socket的 setSoLinger目前我们是设置为0的。设置为这个值的意思是当主动关闭方设置了setSoLinger(true,0)时,并调用close后,立该发送一个RST标志给对端,该TCP连接将立刻夭折,无论是否有排队数据未发送或未被确认。这种关闭方式称为“强行关闭”,而后套接字的虚电路立即被复位,尚未发出的所有数据都会丢失。而被动关闭方却不知道对端已经彻底断开。当被动关闭方正阻塞在recv()调用上时,接受到RST时,会立刻得到一个“connet reset by peer”的异常(即对端已经关闭),c中是返回一个EPEERRST错。

为什么不推崇这种方法在(stevens的unix网络编程卷1 第173页)有详细的讲解。因为TIME_WAIT状态是我们的朋友,它是有助有我们的(也就是说,它会让旧的重复分节在网络中超时消失(当我们的链路越长,ISP复杂的情况下(从网通到教育网的ping包用了9000ms),重复的分节的比例是非常高的。))。而且我们主动关闭连接方大都是由客户端发起的(除了HTTP服务和异常),而且客户方一般都不会有持续的大并发请求。 因此对资源没有这么苛刻要求。

TCP关闭连接(为什么会能Time_wait,Close_wait?)的更多相关文章

  1. [TCP/IP] TCP关闭连接为什么四次挥手

    1.建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端. 2.而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据 ...

  2. tcp关闭连接:挥手讨论

    讨论焦点: 如果client发送FIN后,服务器端未返回完成的数据(缓存区中)还执行返回吗? 参考文档:https://download.csdn.net/download/u013547552/10 ...

  3. TCP的三次握手(建立连接)和四次挥手(关闭连接)

    参照: http://course.ccniit.com/CSTD/Linux/reference/files/018.PDF http://hi.baidu.com/raycomer/item/94 ...

  4. 关于TCP主动关闭连接中的wait_timeout

    首先我们先来回顾一下tcp关闭连接的过程: 假设A和B连接状态为EST,A需要主动关闭: A发送FIN给B,并将状态更改为FIN_WAIT1, B接收到FIN将状态更改为CLOSE_WAIT,并回复A ...

  5. TCP建立连接的3次握手和关闭连接的4次挥手

    #.3次握手过程状态 第一次握手:建立连接时,客户端发送SYN包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认: 第二次握手:服务器收到SYN包,必须确认客户的SYN(ack=j+ ...

  6. TCP三次握手(建立连接)/四次挥手(关闭连接)

    TCP数据包格式 顺序号(32位):用来标识从TCP源端向TCP目的端发送的数据字节流,它表示在这个报文段中的第一个数据字节的顺序号.如果将字节流看作在两个应用程序间的单向流动,则TCP用顺序号对每个 ...

  7. TCP的三次握手(建立连接)和四次挥手(关闭连接)(转)

    转自:(http://www.cnblogs.com/Jessy/p/3535612.html) 参照: http://course.ccniit.com/CSTD/Linux/reference/f ...

  8. 【转载】TCP的三次握手(建立连接)和四次挥手(关闭连接)

    建立连接: 理解:窗口和滑动窗口TCP的流量控制 TCP使用窗口机制进行流量控制 什么是窗口? 连接建立时,各端分配一块缓冲区用来存储接收的数据,并将缓冲区的尺寸发送给另一端 接收方发送的确认信息中包 ...

  9. 深入理解TCP建立和关闭连接

    建立连接: 理解:窗口和滑动窗口TCP的流量控制TCP使用窗口机制进行流量控制什么是窗口?连接建立时,各端分配一块缓冲区用来存储接收的数据,并将缓冲区的尺寸发送给另一端 接收方发送的确认信息中包含了自 ...

随机推荐

  1. Homebrew 的安装方法(官方的方法老师安装失败) 第三方

    官网:http://brew.sh/index_zh-cn.html 安装方式见 官网,在shell里执行如下语句,如下:ruby -e "$(curl -fsSL https://raw. ...

  2. Spring下获取项目根路径--good

    Spring 在 org.springframework.web.util 包中提供了几个特殊用途的 Servlet 监听器,正确地使用它们可以完成一些特定需求的功能.比如某些第三方工具支持通过 ${ ...

  3. r指定位置插入一列

    y<-1:4 data1 <-data.frame(x1=c(1,3,5,7), x2=c(2,4,6,8),x3=c(11,12,13,14),x4=c(15,16,17,18)) da ...

  4. l2正则化

    在机器学习中,无论是分类还是回归,都可能存在由于特征过多而导致的过拟合问题.当然解决的办法有 (1)减少特征,留取最重要的特征. (2)惩罚不重要的特征的权重. 但是通常情况下,我们不知道应该惩罚哪些 ...

  5. WordCount示例深度学习MapReduce过程

    转自: http://blog.csdn.net/yczws1/article/details/21794873 . 我们都安装完Hadoop之后,按照一些案例先要跑一个WourdCount程序,来测 ...

  6. QSignalMapper Class

    /************************************************************************************** * QT QSignal ...

  7. VC++:Debug出错,提示错误在findfile.cpp (Line 369)

    调试程序的Debug版本,出现断言框,定位于库文件findfile.cpp 第369行: void CFileFind::AssertValid() const { // if you trip th ...

  8. JavaScript网页换肤

    使网页背景颜色可选黄/粉 <!doctype html> <html> <head><title>网页换肤</title></head ...

  9. mysqldump如何针对某些数据库进行备份?针对某个数据库进行备份?

    需求描述: 通过mysqldump工具对mysql服务器中的某几个数据库进行备份. 或者就对其中的一个数据库进行备份. 操作过程: 1.通过--databases参数后面加上数据库的名字进行备份 [m ...

  10. [Android系列—] 4. 加入操作栏(Action Bar)

    前言 操作栏是最重要的设计元素之中的一个,使用它来实现你的应用程序活动.通过提供多种用户界面功能, 使应用程序高速和其它的Andorid应用程序一致, 以便被用户熟悉和接受. 主要功能包括: 1. 标 ...