Part 1里提到了TCP的Reliability是通过 checksum 和 seq 机制实现的。
checksum很容易理解,它被广泛应用于各种通信的验证中,通过增加冗余信息来保证传送信息的完整性。
那么我们怎么理解seq这个机制呢?
首先我们先来介绍一下seq
A fundamental notion in the design is that every octet of data sent
over a TCP connection has a sequence number. Since every octet is
sequenced, each of them can be acknowledged. The acknowledgment
mechanism employed is cumulative so that an acknowledgment of sequence
number X indicates that all octets up to but not including X have been
received. This mechanism allows for straight-forward duplicate
detection in the presence of retransmission. Numbering of octets
within a segment is that the first data octet immediately following
the header is the lowest numbered, and the following octets are
numbered consecutively.
TCP对每一个segment都有一个seq。因为有着独一无二(这个等等会解释)的seq,所以接收方可以对每一个segment返回一个ack,来回复发送方自己已经接收到这个segment了。
seq的ack机制是累积的,也就是说你对seq = X的segment进行确认接收后,相当于你对seq <= X的包都确认接收了。
上一篇里我提到过seq可以用来分别一个segment是不是duplicate,这很容易理解,如果我在同一个连接里,收到两个seq一样的segment,我可以直接就丢弃其中任意一个segment,因为我已经对其中一个seq返回一个ack了。
问题的关键就是出在这个同一个连接上,一对socket可以在一个时间点确定一个连接没错,但是socket是可以复用的。host之前和服务器建立了连接A,然后断开连接后过一段时间又建立了一个连接B。用的socket端口都一样,但A连接和B连接明显不是同一个连接。
那么,不是同一个连接的情况下有可能会发生什么呢?
我们来设想一个场景,在一个非常拥塞的网络内,host已经和server建立了一个连接A,host发送给server一个seq = x的segment,但是由于网络原因这个segment并没有被server接收到。这个时候连接A忽然因为server某个原因断开了,可能是内存错误或者其他错误,于是host和server重新建立了一个连接B,好巧不巧也发送了一个seq = x 的segment。
但是这个时候你只能丢弃旧的那个segment了,不能丢弃新的segment,不像之前随便丢弃其中一个都行。这就是delayed duplicate问题。
delay duplicate是一种罕见的特例,但是TCP协议的可靠性要求TCP要能够在这种特例下正常运行。
To avoid confusion we must prevent segments from one incarnation of a
connection from being used while the same sequence numbers may still
be present in the network from an earlier incarnation. We want to
assure this, even if a TCP crashes and loses all knowledge of the
sequence numbers it has been using.
TCP为了保证不产生这种把旧的segment当作新连接的segment的混乱,决定给每个连接初始seq一个比较特殊的值。初始seq的值称之为ISN。
因为每一个segment在网络上传输的时候都是有最大生存周期(Maximum Segment Life)的,我们称之为生存周期T,单位按秒来算。在这个T范围内,一个segment是有可能到达接收方的。过了T范围,可以理解为彻底不存在。一般来说这个T在互联网上是120s。
那么我把ISN和时间挂上钩,确保我在新的连接里使用的ISN不大可能在T秒内重新使用。总之,保证了ISN唯一,那么就基本上能避免delayed duplicate。
当然这不能完全保证,所以TCP还有其他的手段来解决这个问题,比如Quite Time(通常为一个T)。以后找时间补上这一段内容。
every segment emitted occupies one or more sequence
numbers in the sequence space, the numbers occupied by a segment are
"busy" or "in use" until MSL seconds have passed, upon crashing a
block of space-time is occupied by the octets of the last emitted
segment, if a new connection is started too soon and uses any of the
sequence numbers in the space-time footprint of the last segment of
the previous connection incarnation, there is a potential sequence
number overlap area which could cause confusion at the receiver.
至于ISN生成的,TCP RFC里是这么做的
When new connections are created,
an initial sequence number (ISN) generator is employed which selects a
new 32 bit ISN. The generator is bound to a (possibly fictitious) 32
bit clock whose low order bit is incremented roughly every 4
microseconds. Thus, the ISN cycles approximately every 4.55 hours.
Since we assume that segments will stay in the network no more than
the Maximum Segment Lifetime (MSL) and that the MSL is less than 4.55
hours we can reasonably assume that ISN's will be unique.
ISN 生成器4 microseconds自增一次,一个循环4.55小时。而segment的最大生存周期只有120s。所以我们可以姑且认为ISN是唯一的。
当然这个是RFC 793的做法,现在tcp/ip栈实现ISN生成器的方法要复杂不少。
Delayed Duplicate还有一个比较棘手的情况,就是当这个被delayed的segment是用来请求建立连接的segment的时候。
这个时候,一位叫做Tomlinson的科学家引入了大家都非常耳熟能详的 "Three-way handshake" 也就是三次握手。可以说三次握手就是为了解决这种old duplicate connection initialization的情况而设计的。
三次握手情况之一The principle reason for the three-way handshake is to prevent old
duplicate connection initiations from causing confusion.
第五步准确来说不算三次握手的过程。
这里要注意的是,3这一步实际上是两步合作一步,发送了SYN也发送了ACK
2和3 两次通信使得 TCP B得以确认或者说同步TCP A的seq,而3和4让TCP A得以同步TCP B的seq。
同步交换ISN 或者说seq,是三次握手的主要目的。 TCP通过seq保证可靠性,而不是网上那些两次全双工通信确保信道无误的各种奇妙比喻。
回到Delayed Duplicate Initialization的话题上来。
三次握手是怎么处理Delayed Duplicate initialization的呢。
The principle reason for the three-way handshake is to prevent old
duplicate connection initiations from causing confusion. To deal with
this, a special control message, reset, has been devised. If the
receiving TCP is in a non-synchronized state (i.e., SYN-SENT,
SYN-RECEIVED), it returns to LISTEN on receiving an acceptable reset.
If the TCP is in one of the synchronized states (ESTABLISHED,
FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT), it
aborts the connection and informs its user. We discuss this latter
case under "half-open" connections below.
TCP引入了一个reset控制位,如果接收方未同步seq,返回给发送方错误的ack,那么它会接收到一个reset信号,重新变为LISTEN。如果已经同步了seq,那么就会直接终止连接并告诉对方。
处理delayed duplicate initialization在3的时候TCPB收到了一个delayed duplicate initialization 包,于是在4的时候返回了一个错误的ack,TCP A接收到这个ack后意识到不对劲,于是发送给了TCP B一个reset,再然后就恢复正常的三次握手了。
总结一下,三次握手是必须的原因,除开同步两方seq这一点外,还要做到能够处理delayed duplicate initialization。
网友评论