TCP连接过程和状态变化

1. 前言

    关于TCP连接过程与状态,其实网上已经有非常多的好文章了,为什么我还要抽时间写一遍,一是对知识的整理,二是近期在换工作,人生第一次跳槽,觉得应该找个博客网站当港湾,养成知识整理的好习惯,因为,长大了就很难有志同道合的朋友促膝长谈了,应该学会自己撩自己,闲话再撩就多了,下面进入正题。

    本篇文章的立意,不研究TCP传输太底层的原理与实现,我们学习TCP更多的是想为我们在应用层的各项工作提供服务,学以致用才是王道,所以本文重点关注TCP与应用层的紧密关联上、TCP状态对应用层的含义,以及不同系统间TCP通讯时重点关注的状态和调优关注点上。

2. TCP建立连接--三次握手

    TCP建立连接三次握手,必须确认过眼神(“你喜欢我不?”,SYN),确定肯定对方喜欢自己(“Yes, I do.”,ACK),然后才可以放肆互撩(连接建立,ESTABLISHED;传输数据,Write、Read、Write、Read、Write、Read……)。

    其实TCP建立连接的三次握手网上有很多的啦,下面给出我的理解:

2.1 状态解释

CLOSED:虚拟出来的状态,实际不存在,你在netstat -ant | grep 端口的时候是找不到的;

LISTEN:表示Server大门已开,随时准备有Client前来连线;

SYN_SENT:只在Client端出现,表示Client发送过SYN了,正在焦急地等待Server的ACK;

SYN_RCVD:只在Server端出现,表示Server收到Client的SYN了,并且已经发给Client自己的ACK和SYN了,正在焦急地等待Client的ACK;

ESTABLISHED:在Client端出现表示Client把自己的ACK(第3次握手)发出去了,Client已经就绪;在Server端出现表示Server已经收到Client的ACK(第3次握手)了,Server已经就绪;

注:只有在Client和Server同时为ESTABLISHED时,即同时就绪时才可以进行数据传输

2.2 Client端口状态转换

1. 应用层调用connect,发送SYN到对端,等待对端的ACK和SYN;

2. 等待对端的ACK和SYN到来,接收到ACK和SYN后,发出自己的ACK,状态进入ESTABLISHED;

3. 等待对端的ACK和SYN期间,端口状态一直为SYN_SENT,超时后进入CLOSED;

2.3 Server端口状态转换

1. 应用层启动侦听,端口进入LISTEN状态;

2. 接收到Client发来的SYN,发送自己的ACK和SYN,进入SYN_RCVD状态,等待Client的ACK;

3. 等待Client的ACK到来,接收到ACK后,进入ESTABLISHED;等待超时,进入CLOSED;

3. TCP断开连接--四次挥手

    TCP通讯双方,不管是Client还是Server,都可以主动断开连接(分手很简单,有一方就够了),所以下面我们只以主动方和被动方为标注。(实际情况一般由客户端主动关闭,服务端关闭的也有,看到服务端端口状态为TIME_WAIT时不要奇怪)。

3.1 状态解释

FIN_WAIT_1:仅出现在主动方,表示主动方想要断开连接,已经关闭了写通道,并向对端发送了FIN,等待对方的ACK到来;

CLOSE_WAIT:仅出现在被动方,表示被动方收到FIN后,已经回复ACK,正在等自己的应用层调用close方法关闭写通道,在CLOSE_WAIT状态下,自己只能发送数据,但不能接收数据;

FIN_WAIT_2:仅出现在主动方,表示主动方已收到对端的ACK,等待对端的FIN,此时无法再发送数据,但是可以接收数据;

LAST_ACK:仅出现在被动方,表示被动方缓冲区数据已经发送完毕,并且已经发送FIN到对端,等待对端的ACK;如果应用层写的比较垃圾,没有调用close关闭socket,则会一直停留在CLOSE_WAIT;

TIME_WAIT:仅出现在主动方,表示主动方已经发出ACK了,本次通讯完事了,双方都不能再读写了,但是主动方不确定对方能不能收到最后一个ACK,为了保证这个端口释放后,不被后来的连接马上使用被当成是新连接,通俗地讲,为了不乱套,这个状态会一直等待,等多久呢, 等2×MSL(Maximum Segment Lifetime)个时间,这个MSL是操作系统配置的,有默认参数,可以改。

3.2 主动方端口状态转换

1. 应用层调用close方法发起关闭连接;

2. 发送FIN到对端,关闭写通道,端口进入FIN_WAIT_1状态;

3. 等待对端的确认ACK到来,接收到ACK后进入FIN_WAIT_2状态;如果在超时时间内没有收到ACK则直接进入CLOSED状态;

4. 如果在FIN_WAIT_1状态时收到了对端的FIN,则进入CLOSING状态(双发都发出了关闭连接请求,异口同声说“分手”);

5. 如果在FIN_WAIT_2状态时收到了对端的FIN,则进入TIME_WAIT状态;如果在超时时间内没有收这个FIN则直接进入CLOSED状态;

6. 在TIME_WAIT状态等待2个MSL(报文最长存活周期)后进入CLOSED状态;

3.3 被动方端口状态转换

1. 收到对端的FIN后,关闭读通道,自己进入CLOSE_WAIT状态;

2. 在CLOSE_WAIT状态等待应用层调用close方法关闭socket连接;

3. 如果在超时时间内没调用close,则直接进入CLOSED状态;

4. 如果在超时时间内调用了close,则向对端发送FIN,自己进入LAST_ACK状态,等待对端的ACK;

5. 等待对端的ACK,如果在超时时间内收到了ACK则直接进入CLOSED状态,否则超时后进入CLOSED状态;

4. 调优

    上边已经提到了,服务端有时也会主动关闭连接,应该是为了避免有些不地道的Client在撩完以后不关闭socket连接,一直占用服务器资源,所以有些服务器也会无情地主动关闭连接,为了自己不被干倒。

    在高并发场景下,服务器主动关闭连接的情况,在服务端会出现大量的TIME_WAIT状态的端口,会占用操作系统文件句柄资源,导致新的连接可能无法建立,影响高并发性能,而关于TCP参数的调优中,有一大堆,但有一些参数调优对业务是有风险的,所以这里只给出一个相对比较安全的推荐调优,源自《码出高效  阿里巴巴Java开发手册 终极版 (1.3.0)》。

    在Linux服务器上,/etc/sysctl.conf文件中修改time_wait缺省值(默认240秒):

net.ipv4.tcp_fin_timeout = 30

注:记得用root修改,修改后执行sysctl -p使参数立即生效。

5. 闲撩

    实际工作中,在编写网络应用程序时,一般不会自己去封装Socket底层通讯框架,我们往往喜欢站在巨人的肩膀上,这样可以少点重复造轮子,借鉴前人的东西会提升不少工作效率,使用前人的东西为自己深度定制。以Java为例,一般我们使用NIO,但不直接使用JDK的NIO通讯包,目前比较流行的是Netty,有时间再出个Netty的实战例子。

 

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值