tcp out of order是怎么回事_网络编程:TCP协议浅析

前言

了解TCP协议的细节,有助于调试socket的问题,有时还能使程序更高效。但是TCP协议实在太复杂了,这里只能作一个总体框架的描述,真要钻究每一个细节,需要看其他专业的书,比如TCP/IP Illustrated。

TCP段格式

TCP将传输的数据分成多个段(segment),一个段用一个IP数据报传输,段的格式如下:

e6eb0550d406b7ce87ccd839dfa1d3da.png
tcp数据段格式
  • Source port number 这是发送端的端口号
  • Destination port number 这是接口端的端口号
  • Sequence number 序列号,每一个段都有一个一直增长序列号,用于表示已经发送的有效数据,等下详述,
  • Acknowledgement number 确认号,如果ACK位有设(见下面的Control),这里就是对收到的段的回应,它表示下次想收到的序列号值。
  • Header length 段头的长度,4位,单位是4个字节,即可以容纳64字节的头。
  • Reserved 保留位,必须为0。
  • Control bits 控制位,可以表示8种控制码,这里只关注下面几种:
    • ACK 确认号,如果有设置, acknowledgement number包含有效信息,表示这个发送段是上一个接收段的回应。
    • SYN 同步序列号,对应Sequence number的信息。
    • FIN 发送端用它表明已完成数据传输,用于断开连接的处理。
    • RST 用于处理各种错误情况。
    • URG 如果设置,Urgent pointer包含有效信息。
  • Window size 窗口大小,这是接收端发送确认包的时候,告诉发送端我这里还有多少空间可用于接收数据。
  • Checksum 16位校验码。
  • Urgent pointer 紧急数据指针,指向流中紧急数据(Out-of-Band Data)的位置。
  • Options TCP连接选项,这是一个可变长度,通过Header length和其他确认长度的字段,可以得到这个长度。
  • Data 有效数据,这和上层协议相关。

序列号和确认号

  • TCP通信的两端各自维护着一个序列号,在连接建立之时,两端都会生成一个初始序列号,这个序列号不是从0或1开始,而是32位整型中的一个值。
  • 发送端每次发送数据段,都会带着这个序列号,它其实是表示通信开始后,发送过的字节流的偏移,比如当前段的序列号是10,有效数据长度是100,那么,下一个段的序列就是110,以此类推。
  • 发送端发送的每一个数据段,接收端都要回应一个确认段,确认号就是收到的数据段的序列号加上数据长度,它向发送端表明我已经收到了这么多数据。
  • 序列号和确认号是为了解决网络包乱序,丢包,包重复等问题,也是TCP可靠传输的基础。

TCP状态机

TCP连接的过程就是一个状态机,从握手到交换数据到结束,两端的socket在个各个状态切换,下图是TCP状态的切换过程:

d021c60bdeef317737c971fa94bfe744.png
TCP状态机

如何看懂这个状态图:

  • 虚线箭头一般是服务器的状态转换,实线箭头一般是客户端的状态转换。
  • 粗体是程序执行的动作。
  • send: 发送数据段。
  • recv: 从对端收到数据段。

各个状态的含义是:

  • CLOSED TCP端点初始的状态。
  • LISTEN 服务器socket调用listen函数之后,处于这个状态,等待对端的连接请求。
  • SYN_SENT 客户端主动连接,发送SYN包给客户端,并等待服务器ACK。
  • SYN_RECV 前面处于LISTEN状态的TCP,收到客户端TCP发送的SYN包,并且返回SYN/ACK包,此时处于这个状态,它要等待客户端发送ACK。
  • ESTABLISHED 从服务器看,收到客户端的ACK包后,TCP过度这个状态;从客户端看,客户端发送了ACK包后,过度到这个状态。这就表示双方已经握手成功,现在可以交换数据了。
  • FIN_WAIT1 程序主动关闭(close)连接,此时发送一个FIN包给对端,并等待对端的ACK,处于这个状态。
  • FIN_WAIT2 上面处于FIN_WAIT1的TCP收到对端的ACK,过度到这个状态。
  • TIME_WAIT 程序主动关闭之后,收到对端发送的FIN包,提示对端已经执行被动关闭,此时到这个状态,在这个状态下会保持一定的时间(2MSL)后,才变成CLOSED。
  • CLOSING 程序主动关闭并发送FIN包给对端,与此同时,对端也主动关闭向这边发送FIN包(这是很少见的情况);此时到会这个状态,这边同时发送ACK给对端。接下来对端因为前面的FIN包返回ACK包,这边就从CLOSING过度到TIME_WAIT状态。主动关闭的状态是非常复杂的。
  • CLOSE_WAIT 服务器收到客户端的FIN包,它返回ACK包,然后处于这个状态。
  • LAST_ACK 接着执行被动关闭(close),向客户端发送FIN包,然后处于这个状态。最后收到客户端的ACK包,过度到CLOSED状态,被动关闭相对简单得多。

三次握手,四次挥手

上面这些状态看起来很复杂,其实细分一下就是三个阶段,建立连接阶段,交换数据阶段,断开连接阶段。

其中在ESTABLISHED之前为建立连接阶段,也就是三次握手:

f030784a2e490dc843506e9cde4b6cc5.png
TCP 3次握手
  1. 客户端发送SYN给服务器,M为初始序列号。
  2. 服务器收到后向客户端发送一个包,ACK M+1表示回应上面的SYN,SYN N则是服务器的初始序列号;所以这个包其实做了两件事件,一件是确认,另一个是告诉对方我的初始序列号。
  3. 客户端收到后向服务器发送ACK N+1,表示我知道了。

为什么是三次握手,原因就是为了告诉双方初始序列号是多少,并且因为TCP规定必须有ACK包,所以最少要三次。如果中间那次分成两个包也行,但那样就需要来回4次了。

假如握手前2步完成了,第3步客户端没发送ACK包会发生什么?服务器会认为是丢包了,然后重新发送SYN/ACK包给客户端,在Linux下,默认重试次数为5次,重试的间隔时间从1s开始每次都翻售,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后还要等32s才知道第5次也超时了,所以总共需要63s,TCP才会把断开这个连接。

有人利用这个漏洞,向服务器发送SYN包后就下线,服务器要等63秒才断开连接,当大量的SYN包把服务器的SYN连接队列填满后,正常的连接请求就没法处理了,这种攻击方式就是syn flood攻击。至于怎么应对,还需要看其他方面的文档:)。

接下来看看4次挥手,也就是断开连接的4次交互:

9911135d62929b98649b57829dc0b3be.png
TCP 4次挥手
  • 通常客户端主动调用close关闭连接,它向服务器发送FIN包;服务器收到后回应ACK。
  • 然后服务器也调用close,这个叫被动关闭,它向客户端FIN包;客户端收到后回应ACK。

这个过程其实就是两次来回,双方分别关闭。为什么不像握手那样合并成3次,因为close是程序的行为,只有程序调用close,才会发送FIN包,所以没有办法和前面的ACK合并。

TIME_WAIT状态

TCP主动关闭一方将会进入TIME_WAIT,这个状态需要维持2MSL时间才会断开。MSL叫最大段生命周期,也就是一个TCP段在网络存活的最大时间。RFC 1122建议是2分钟,但BSD sockets的实现把MSL设定为30秒。2MSL也就是60秒。为什么主动关闭一方需要维持2MSL再关闭呢:

  1. 实现可靠的中断:被动方(server)发送FIN后,主动方(client)回应ACK,如果这个ACK丢了,被动方还会重新发送FIN,TIME_WAIT状态就是预留足够的时间应对中断时的丢包重传。
  2. 避免新的连接接受网络中旧的重复包:TCP由于重传机制,可能会产生重复包,且这些包可能会在连接断开后才到达;如果刚好有一个新的连接使用同样的IP/port,这些重复包就会传给新的连接,TCP必须避免这种情况发生,它的处理方法是如果TCP端处于TIME_WAIT状态,无法建立新的连接。

如果服务器bind一个地址,这个地址刚好有一个TCP处于TIME_WAIT的状态,bind会返回EADDRINUSE(Address already in use)错误,这种情况出现在,服务器与客户端的连接socket执行了主动关闭,这个socket的TCP会处于TIME_WAIT状态,然后服务器重启尝试去bind这个地址,系统默认不允许bind, 解决办法是对这个socket使用SO_REUSEADDR选项。

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值