TCP知识1

来源:互联网 发布:d3.js 力学图 编辑:程序博客网 时间:2024/06/05 19:18

1、TCP数据被封装在一个IP数据报中,如图所示:


其中TCP的格式是:



  • 源端和目的端的端口号:用于TCP的多路复用和多路分解,即标识发端和收端应用进程。
  • 序号:标识字节流。TCP用序号对字节流中的每个字节进行计数,一个报文段的序号被设置为该报文段中第一个数据字节的计数值。序号是32bit的无符号数,序号到达232-1后又从0开始。当建立一个新的连接时,SYN标志变1。序号字段包含由这个主机选择的该连接的初始序号ISN(InitialSequenceNumber)。该主机要发送数据的第一个字节序号为这个ISN加1,因为SYN标志消耗了一个序号。由于TCP提供的是全双工的服务即连接双方可以同时独立地发送数据,因此连接的每一端必须保持每个方向上的传输数据序号。
  • 确认序号:确认序号包含发送确认的一端所期望收到的下一个序号。因此,确认序号应当是上次已成功收到数据字节序号加1。只有ACK标志1时确认序号字段才有效。发送ACK无需任何代价,因为32bit的确认序号字段和ACK标志一样,总是TCP首部的一部分。因此一旦一个连接建立起来,这个字段总是被设置,ACK标志也总是被设置为1。
  • 首部长度:首部中32bit字的数目。这个字段占4bit,因此TCP最多有60字节的首部。正常的长度是20字节。
  • 6个标志比特:
    • URG紧急指针有效。
    • ACK确认序号有效。
    • PSH接收方应该尽快将这个报文段交给应用层。
    • RST重建连接。
    • SYN同步序号用来发起一个连接。
    • FIN发端完成发送任务。
  • 窗口大小:通告给对端的本段窗口大小。用于流量控制。
  • 检验和:整个TCP报文段的校验和,覆盖了TCP首部和TCP数据。必须给出,由发送端设置,接收端验证。类似于UDP的校验和,校验和计算是用了和UDP相同的一个伪首部。
  • 紧急指针:只有当URG标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。
  • 选项:包括TCP所支持的一些选项。最常见的可选字段是最长报文大小,又称为MSS(MaximumSegmentSize)。每个连接方通常都在通信的第一个报文段(为建立连接而设置SYN标志的那个段)中指明这个选项。它指明本端所能接收的最大长度的报文段。
2、 2MSL等待状态

TCP中有一个报文段最大生存(MSL,MaximumSegmentLifetime)时间的概念。RFC793[Postel1981c]指定MSL为2分钟。然而,实现中的常用值是30秒,1分钟,或2分钟。
TIME_WAIT状态也称为2MSL等待状态。它是指:当TCP连接的一方执行主动关闭时,在它方完最后一个ACK(即对对端FIN的ACK)后,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样的目的是可以让TCP再次发送最后的ACK以防这个ACK丢失(另一端超时并重发最后的FIN)。

假设没有TIME_WAIT状态,考虑主机A和主机B之间有一个TCP连接。
  1. 主机A执行主动关闭,并且收到了主机B的FIN报文,由于不存在TIME_WAIT状态,因而主机A一端的该TCP连接进入关闭状态。
  2. 由于某种原因, 主机B没有收到主机A对其FIN的确认,因而其超时并重传了最后的FIN。
  3. 主机A以与步骤1中关闭的TCP相同的四元组与主机B重新建立了一个TCP连接。
  4. 主机A收到了主机B针对第一步中被关闭的TCP连接的FIN。这个FIN有可能导致步骤3中刚建立的TCP连接被关闭(因为每个TCP连接的ISN是随机选取的,因而即便在这种场景下,大部分时候FIN也会由于序列号不匹配而被丢失,但是无论如何总是存在旧的FIN将新的连接关闭的危险)。
2MSL等待导致当一个TCP连接处于该状态时,该连接的socket使用的四元组不能被重新使用,即不可能在这个时间段内再重建具有相同四元组的socket。实现中这种限制甚至更为严重--该socket的所使用的本地端口将不能被重新使用。
在连接处于2MSL等待时,任何迟到的报文段将被丢弃。
需要注意的是只有主动关闭的一端会进入该状态,被动关闭的一端不会进入该状态。由于这个特性,所以应该尽量让客户端执行主动关闭,服务器执行被动关闭。因为客户端的端口是可以随意选取的,而服务器必须使用为客户端所知道的端口,因而该状态对客户端影响很小,但是如果是服务器进入了该状态就会影响较大。

3、平静时间

2MSL等待状态可以防止将来自一个连接较早实例(即四元组相同的连接)的迟到的报文段解释为新连接的一部分。
但如果进入2MSL等待状态的主机由于故障重启了并且在MSL内完成重启,并使用相同的信息建立了一个应该处于2MLS等待状态的连接。这时在故障前从这个连接发出的迟到的报文段会被错误地当作属于重启后新连接的报文段。无论如何选择重启后新连接的初始序号,都会发生这种情况。
为了防止这种情况,RFC793指出TCP在重启动后的MSL秒内不能建立任何连接。这就称为平静时间(quiettime)。只有极少的实现版遵守这一原则,因为大多数主机重启动的时间都比MSL秒要长。

4、FIN_WAIT_2状态
FINWAIT2状态中本端已经发出了FIN,并且另一端也已对它进行确认。如果是半关闭,则到这一步就可以结束了,但是如果不是半关闭,则会等待对端发送FIN来关闭另一个方向的数据传输,此时对端处于CLOSEWATI状态。但是如果对端不发送FIN,本端就一直处于FINWAIT2状态,而对端也将处于CLOSEWAIT状态,并一直保持直到对端决定进行关闭。
linux实现通过shutdown来支持半关闭,而close则用来执行一个全关闭(即期望对端也关闭其在另一个方向的发送)。而在在linux实现中,如果执行的是全关闭,则会设置一个超时间来防止连接在FIN_WAIT_2状态一直等待(这个实现来自于伯克利系统,并且注视表明其是违反RFC的)。


5、建立连接

TCP通过三次握手完成建立连接的工作:

  1. 连接发起者发送一个SYN段指明自己期望连接的对端的端口,以及初始序号。这个SYN段为报文段1。
  2. 对端发回包含对端的初始序号的SYN报文段(报文段2)作为应答。同时,将确认序号设置为连接发起者的ISN加1以对连接发起者的SYN报文段进行确认。一个SYN将占用一个序号。
  3. 连接发起者必须将确认序号设置为对端的ISN加1以对对端的SYN报文段进行确认(报文段3)。
这三个报文段完成连接的建立。这个过程也称为三次握手(three-wayhandshake)。
发送第一个SYN的一端将执行主动打开(activeopen)。接收这个SYN并发回下一个SYN的另一端执行被动打开(passiveopen)。
发送SYN时需要指定ISN。ISN是随时间而变化的,因此每个连接都将具有不同的ISN。这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它作错误的解释。
建立连接时,当两端的SYN都被确认时,TCP连接的双方就可以确认对端已经同意与自己建立连接,并且使用自己SYN报文中携带的信息建立连接,因而在收到SYN的确认后,每一端都可以安全的为连接分配所需的所有资源。两端都发送SYN并收到SYN的确认经过三个报文的交互已经足以完成,因而TCP使用三次握手来建立连接。

6、终止连接
TCP是全双工的,因而每个方向的传输都必须单独进行关闭。TCP允许一个方向的传输被关闭,而另一个方向的传输不关闭,这就是TCP的半关闭。由于TCP支持半关闭,因而关闭一个连接需要4次握手。
当连接的一端完成它的数据发送任务后它就可以发送一个FIN来终止这个方向的连接,这意味着本端将不会再向对端发送数据。当一端收到一个FIN后,它必须通知应用层另一端已经终止了那个方向的数据传送,但是这一端仍能发送数据。程序这样做。正常关闭过程:
  1. 首先进行关闭的一方(即发送第一个FIN)将执行主动关闭
  2. 而另一方(收到这个FIN)执行被动关闭。通常一方完成主动关闭而另一方完成被动关闭。
和SYN一样,一个FIN将占用一个序号。通常情况下,TCP连接都由客户端发起和终止。
7、TCP支持同时打开和关闭

8、终止一个连接
终止一个连接的正常方式是一方发送FIN,这种方式被称为有序释放。在这种方式中,FIN在所有排队数据都发送后才会被发送,一般不会有任何数据丢失。但是连接的一方也可以选择通过RST来释放一个连接,这被称为异常释放。异常终止一个连接对应用程序来说有两个优点:
  1. 直接丢弃任何待发数据并立即发送复位报文段,无需ACK对RST报文段进行确认。
  2. RST的接收方会区分另一端执行的是异常关闭还是正常关闭。并可以通知应用程序。
9、最大报文段长度
最大报文段长度(MSS)表示TCP传往另一端的最大块数据的长度。当一个连接建立时,连接的双方都要通告各自的MSS。当建立一个连接时,每一方都有用于通告它期望接收的MSS选项(MSS选项只能出现在SYN报文段中)。如果一方不接收来自另一方的MSS值,则MSS就定为默认值536字节(这个默认值允许20字节的IP首部和20字节的TCP首部以适合576字节IP数据报)。
MSS越大对于IP和TCP来说网络利用率越高(因为头部占的开销比例就下降了)。因此如果可以确保不发生分片,则MSS越大越好。但MSS也并不是越大越好:
  • MSS越大单个报文段的错误造成的影响也越大,TCP会进行重传,如果一个报文段发生了错误,则它会重传该报文段,而如果使用的是较小的MSS,则重传的报文段就更小,因而相比而言大的MSS的重传浪费的带宽更多
  • 因特网中路由器采用的是存储转发方式,因而大的MSS会导致单点处理单个报文的时间变长。TCP采用了滑动窗口机制,可以认为是一种流水线工作方式。假设要使用TCP连接传输的数据长度为leng字节,单个报文段到达目的地需要a+1个路由器处理时间,连接之间的速率都为v。则

该流水线中路由器单点处理时间为:(MSS + 40 )/v

所有数据都到达目的所需要经过的路由器处理时间个数为:(length/MSS + a),因为根据流水线的性质,处理一个报文段需要x个处理间隔的话,处理n个需要x + n -1个处理间隔。

则这些数据全部到达目的地需要的总时间为:((MSS + 40 )/v) * ((length/MSS + a)),增大MSS到MSS+x,则需要的时间为((MSS + x + 40 )/v) * ((length/(MSS +x)+ a))。让具有较小MSS的总时间减去较大MSS的,则可得到

((MSS + 40 )/v) * ((length/MSS + a)) - ((MSS + x + 40 )/v) * ((length/(MSS +x)+ a))=( (len * 40) / v ) * (x / (MSS * (MSS +x))) – x * a / v

考察该式子和0的关系,由于这些假设的变量里没有为0的,因而我们可以提出公因子v并忽略它,然后可以得到式子:

(  (len * 40)  - a * MSS * MSS – a * MSS * x  ) * x / (MSS * MSS + MSS *x)

进一步忽略正直x / (MSS * MSS + MSS *x),只需要考察

(len * 40)  - a * MSS * MSS – a * MSS * x与0的大小即可。因此当

  • x小于(len * 40) / (a * MSS) – MSS时,表达式大于0,增大x处理时间变短
  • x等于(len * 40) / (a * MSS) – MSS时,处理时间不变
  • x大于(len * 40) / (a * MSS) – MSS时,表达式小于0,增大x处理时间变长

显然,这里的a和len是变量,几种可能都有,因此从理论上来说MSS并不是越大越好。

MSS让主机限制另一端发送数据报的长度,而主机也能控制它发送数据报的长度,因此主机可以使用该方式来尽可能避免分段,但是这不足以避免分段,因为连接双发都无法知道连接路径上经过的路由器的MTU限制,使用路径MTU发现机制才可以发现路径的MTU从而获得一个可以避免被分片的MSS值,这也是IPv6使用的方法。


0 0
原创粉丝点击