文章目录
TCP的服务
- 应用数据被分割成TCP认为最适合发送的数据块
- 当TCP发送一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
- 当TCP收到来自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒。
- TCP将保持它首部和数据的校验和
- 既然TCP报文段作为IP数据包来传输,而IP数据包的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。
- 既然IP数据包可发生重复,TCP的接收端必须丢弃重复的数据。
- TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。
- TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机导致较慢主机的缓冲区溢出。
TCP首部
TCP数据被封装在一个IP数据包中
TCP首部的数据格式,前20个字节。
- 每个TCP段都包含源端和目的端的端口号,用于寻找发端和收端应用进程。 这两个值加上IP首部中的源IP地址和目的IP地址唯一确定一个TCP连接。
- 一个IP地址和一个端口号也成为一个socket。socketpair(包含源IP地址、源端口号、目的IP地址和目的端口号)可唯一确定互联网络中每个TCP连接的双方。
- 序号用来标识从TCP发送端向TCP收端发送的数据字节流。它表示在这个报文段中的第一个数据字节。如果将字节流看作在两个应用程序间的单向流动,则TCP用序号对每个字节进行计数。
- 确认序号应当是已成功收到数据字节序号加1。只有ACK标志尾1时,确认序号字段才有效。
- TCP为应用层提供全双工服务。这意味着数据能在两个方向上独立的进行传输。因此连接的每一端必须保持每个方向上的传输数据序号。
- TCP可以表述为一个没有选择确认或否认的滑动窗口协议。 我们说TCP缺少选择确认是因为 T C P首部中的确认序号表示发方已成功收到字节,但还不包含确认序号所指的字节。当前还无法对数据流中选定的部分进行确认。例如,如果1~1024字节已经成功收到,下一报文段中包含序号从 2049~3072的字节,收端并不能确认这个新的报文段。它所能做的就是发回一个确认序号为 1025的A C K。它也无法对一个报文段进行否认。例如,如果收到包含 1025~2048字节的报文段,但它的检验和错, TCP接收端所能做的就是发回一个确认序号为 1025的ACK。
- 在TCP首部中有6个标志比特。它们中的多个可同时被设置为 1。
URG 紧急指针(urgent pointer)有效。
ACK 确认序号有效。
PSH 接收方应该尽快将这个报文段交给应用层。
RST 重建连接。
SYN 同步序号用来发起一个连接。
FIN 发端完成发送任务。 - TCP的流量控制由连接的每一端通过声明的窗口大小来提供。
- 检验和覆盖了整个的TCP报文段:TCP首部和TCP数据。这是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。 TCP检验和的计算和 U D P检验和的计算相似,使用一个伪首部。
- 只有当URG标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。
- 最常见的可选字段是最长报文大小,又称为 MSS (Maximum Segment Size)。每个连接方通常都在通信的第一个报文段(为建立连接而设置 S Y N标志的那个段)中指明这个选项。它指明本端所能接收的最大长度的报文段。
- TCP报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有TCP首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情中,也会发送不带任何数据的报文段。
TCP连接的建立与终止
建立连接协议
- 请求端(通常称为客户)发送一个 SYN(同步序号)段指明客户打算连接的服务器的端口,以及初始序号(ISN,在这个例子中为1415531521)。这个SYN段为报文段1。
- 服务器发回包含服务器的初始序号的 SYN报文段(报文段2)作为应答。同时,将确认序号设置为客户的ISN加1以对客户的SYN报文段进行确认。一个SYN将占用一个序号。
- 客户必须将确认序号设置为服务器的 ISN加1以对服务器的S Y N报文段进行确认(报文
段3)
发送第一个SYN的一端将执行主动打开(active open)。接收这个SYN并发回下一个SYN的另一端执行被动打开(passive open)。
连接终止协议
- 建立一个连接需要三次握手,而终止一个连接需要四次握手。这由TCP的半关闭造成的。既然一个TCP连接是全双工的,因此每个方向必须单独进行关闭。这原则就是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。当一端收到一个FIN,它必须通知应用层另一个端已经终止了那个方向的数据传送。发送FIN通常是引用层进行关闭的结果。
- 收到一个FIN只意味着在这一个方向上没有数据流动。一个 TCP连接在收到一个 FIN后仍能发送数据。
- 当服务器收到FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
TCP状态迁移图
TCP正常连接建立和终止所对应的状态
2MSL等待状态
- TIME_WAIT状态也称为2MSL状态。每个具体TCP实现必须选择一个报文段最大生存时间MSL(Maximum Segment Lifetime)。它是任何报文段被丢弃前在网络内的最长时间。RFC支持MSL为两分钟。然而,实现中的常用值为30秒、1分钟或两分钟。
- 对一个具体实现所给定的MSL值,处理的原则是:当TCP执行一个主动关闭,并发回最后一个ACK,该连接必须在TIME_WAIT状态停留的时间为2倍MSL。这样可让TCP再次发送最后的ACK以防这个ACK丢失(最后的ACK可能会超时或者丢包)。
- 这种2MSL等待的另一种结果是这个TCP连接在2MSL等待期间,定义这个连接的socket(源IP和端口号、目的IP和端口号)不能再被使用。这个连接只能在2MSL借宿后再被使用。
- 客户执行主动关闭并进入 TIME _ WAIT是正常的。服务器通常执行被动关闭,不会进入TIME _ WAIT状态。
FIN_WAIT_2状态
在FIN _ WAIT _ 2状态我们已经发出了 FIN,并且另一端也已对它进行确认。除非我们在实行半关闭,否则将等待另一端的应用层意识到它已收到一个文件结束符说明,并向我们发一个FIN来关闭另一方向的连接。只有当另一端的进程完成这个关闭,我们这端才会从FIN _ WAIT _ 2状态进入TIME _ WAIT状态。这意味着我们这端可能永远保持这个状态。另一端也将处于 CLOSE _ WAIT状态,并一直保持这个状态直到应用层决定进行关闭。
许多伯克利实现采用如下方式来防止这种在FIN _ WAIT _ 2状态的无限等待。如果执行主动关闭的应用层将进行全关闭,而不是半关闭来说明它还想接收数据,就设置一个定时器。如果这个连接空闲10分钟75秒,TCP将进入CLOSED状态。
流量控制
快的发送方和慢的接收方
- 发送方连续发数据,如果发的太快,接收方会发送ACK(报文段8)通知其窗口大小为0,这说明接收方已经收到所有数据,但这些数据都在接收方的TCP缓冲区,因为应用程序还没有读取这些数据。
- 另一个ACK(成为窗口更新), 在17.4ms之后发送,表明接收方现在可接收另外的4096字节的数据。虽然这看起来像一个ACK,但由于他并不确认任何新数据,只是用来增加窗口的右边沿,因此被称为窗口更新。
滑动窗口
- 接收方通告的窗口称为提出的窗口,它覆盖了从第4字节到第9字节的区域。表明接收方已经确认了包括第3字节在内的数据,且通告窗口大小为6。
- 当接收方确认数据后,这个滑动窗口不时地向右移动。窗口两个边沿的相对运动增加或者减少了窗口的大小。
称窗口左边沿向有边沿靠近称为窗口合拢。这种现象发生在数据被发送和确认但没有被应用进程读走时。
当窗口右边沿向右移动时将允许发送更多的数据,我们称之为窗口张开。这种现象发生在另一端的接收进程读取已经确认的数据并释放了TCP的接受缓存时。
如果左边沿到达右边沿,则称其为一个零窗口,此时发送方不能发送任何数据。
拥塞控制
TCP的四种拥塞控制算法
- 慢开始
- 拥塞避免
- 快重传
- 快恢复
慢开始和拥塞避免
- 发送方维护一个叫做拥塞窗口cwnd(congestion window)的状态变量,其值取决于网络的拥塞程度,并且动态变化。(拥塞窗口是发送发使用的流量控制,通告窗口是接收方使用的流量控制)。
拥塞窗口cwnd的维护原则:只要网络没有出现拥塞,拥塞窗口就再增大一些;但只要网络出现拥塞,拥塞窗口就减少一些。
判断网络出现拥塞的依据:没要按时收到应当到达的确认报文(即发生超时重传) - 发送方将拥塞窗口作为发送窗口swnd,即swnd = cwnd。
- 维护一个满开始门限ssthresh状态变量
当cwnd<ssthresh时:使用慢开始算法
当cwnd>ssthresh时:停止使用慢开始算法而改用拥塞避免算法
当cwnd=ssthresh时:既可以使用慢开始算法也可以使用拥塞避免算法
- “慢开始”是指一开始向网络注入的报文段少,并不是指拥塞窗口cwnd增长速度慢
- “拥塞避免”并非完全能够避免拥塞,而是指再拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞
快重传和快恢复
快重传
有时,个别报文段会在网络中丢失,但实际上网络并未发生拥塞
这将导致发送方超时重传,并误认为网络发生了拥塞
发送方把拥塞窗口cwnd又设置为1,并错误地启动慢开始算法,因而降低了传输效率
- 采用快重传算法可以让发送方尽早知道发生了个别报文段的丢失
- 所谓快重传,就是使发送方尽快进行重传,而不是等待超时重传计时器超时再重传。
要求接收方不要等待自己发送数据时才进行捎带确认,而是要立即发送确认
即使收到了失序的报文段也要立即发出对已收到的报文段的重复确认
发送发一旦收到了3个连续的重复确认,就将相应的报文段立即重传,而不是等待该报文段的超时重传计数器超时再重传。
快恢复
发送方一旦收到3个重复确认,就知道现在只是丢失了个别的报文段。于是不启动慢开始算法,而执行快恢复算法。
发送方将慢开始门限ssthresh值和拥塞窗口cwnd值调整为当前窗口的一半;开始执行拥塞避免算法
也有的快恢复实现是把快恢复开始时的拥塞窗口cwnd值再增大一些,即等于新的ssthresh + 3