传输层协议之TCP协议
传输层最重要的协议为TCP协议和UDP协议。TCP协议复杂且面向连接,但传输可靠。UDP协议简单且不面向连接,传输不可靠。
TCP比较出众的一点就是提供一个可靠的,流控的数据传输。而传输层的出现有个重要的原因:端口。
IP协议进行的是IP地址到IP地址的传输,这意味者两台计算机之间的对话。但每台计算机中需要有多个通信通道,并将多个通信通道分配给不同的进程使用。一个端口就代表了这样的一个通信通道。网络层在逻辑上提供了端口的概念。一个IP地址可以有多个端口。一个具体的端口需要IP地址和端口号共同确定(我们记为IP:port的形式)。
TCP协议的两个特性:
一、双向连接:
TCP连接是双工(duplex)的。双向连接实际上就是建立两个方向的TCP传输。因此,主机与服务器的交互可能是双向的~
二、TCP端口号
TCP允许一个主机同时运行多个应用进程。每台主机可以拥有多个应用端口,每对端口号、源和目标IP地址的组合唯一地标识了一个会话。端口分为知名端口和动态端口。 有些网络服务会使用固定的端口,这类端口称为知名端口,端口号范围为0-1023。动态端口号范围从1024到65535
TCP 头部:
TCP通常使用IP作为网络层协议,这时TCP数据段被封装在IP数据包内。TCP数据段由TCP Header(头部)和TCP Data(数据)组成。TCP最多可以有60个字节的头部,如果没有Options字段,正常的长度是20字节。
TCP Header是由上图标识一些字段组成,下面是对字段的介绍:
16位源端口号:源主机的应用程序使用的端口号。
16位目的端口号:目的主机的应用程序使用的端口号。每个TCP头部都包含源和目的端的端口号,这两个值加上IP头部中的源IP地址和目的IP地址可以唯一确定一个TCP连接。
32位序列号:用于标识从发送端发出的不同的TCP数据段的序号。数据段在网络中传输时,它们的顺序可能会发生变化;接收端依据此序列号,便可按照正确的顺序重组数据。
32位确认序列号:用于标识接收端确认收到的数据段。确认序列号为成功收到的数据序列号加1。
4位头部长度:表示头部占32bit字的数目,它能表达的TCP头部最大长度为60字节。
16位窗口大小:表示接收端期望通过单次确认而收到的数据的大小。由于该字段为16位,所以窗口大小的最大值为65535字节,该机制通常用来进行流量控制。
16位校验和:校验整个TCP报文段,包括TCP头部和TCP数据。该值由发送端计算和记录并由接收端进行验证。
TCP 三次握手
建立起一个TCP连接需要经过“三次握手”:
- 第一次握手:客户端发送请求建立连接报文(seq = x,同时SYN置1,表示第一次建立连接)到服务器,并进入SYN_SENT状态,等待服务器确认;
- 第二次握手:服务器收到SYN包,回复客户的请求报文(seq =y,ack=x+1),同时回包,即SYN+ACK包(SYN、ACK置1,ACK置1表示同意建立连接),此时服务器进入SYN_RECV状态;
- 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”
TCP四次挥手:
- 我们可以看到,连接终结的过程中,连接双方也交换了四片信息(两个FIN和两个ACK)。TCP并没有合并FIN与ACK片段。原因是TCP连接允许单向关闭(half-close)。
- 假设Client端发起中断请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说“我client端要发给你了”,但是如果你还没有数据要发送完成,则不必急着关闭Socket,可以继续发送数据。所以所以你先发送ACK,”告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息”。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。
- 当Server端确定数据已发送完成,则向Client端发送FIN报文,”告诉Client端,好了,我这边数据发完了,准备好关闭连接了”。Client端收到FIN报文后,”就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,”就知道可以断开连接了”。Client端等待了2MSL(最大报文段生存时间)后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。
Ok,TCP连接就这样关闭了!
“流”通信
TCP协议是传输层协议,实现的是端口到端口(port)的通信。更进一步,TCP协议虚拟了文本流(byte stream)的通信。在TCP协议与”流”通信中讨论的TCP传输需要一个前提:TCP连接已经建立。
IP协议和UDP协议采用的是数据包的方式传送,后发出的数据包可能早到,我们并不能保证数据到达的次序。
TCP协议确保了数据到达的顺序与文本流顺序相符。当计算机从TCP协议的接口读取数据时,这些数据已经是排列好顺序的“流”了。比如我们有一个大文件要从本地主机发送到远程主机,如果是按照“流”接收到的话,我们可以一边接收,一边将文本流存入文件系统。这样,等到“流”接收完了,硬盘写入操作也已经完成。如果采取UDP的传输方式,我们需要等到所有的数据到达后,进行排序,才能组装成大的文件。
片段与编号
我们知道了TCP是通过报文流进行通信的,那么同时发出的多个报文流,对端是如何按正确的顺序进行组装呢?
TCP片段的头部(header)会存有该片段的序列号(sequence number)。这样,接收的计算机就可以知道接收到的片段在原文本流中的顺序了,也可以知道自己下一步需要接收哪个片段以形成流。比如已经接收到了片段1,片段2,片段3,那么接收主机就开始期待片段4。如果接收到不符合顺序的数据包(比如片段5),接收方的TCP模块可以拒绝接收,从而保证呈现给接收主机的信息是符合次序的“流”。
TCP 可靠性
- TCP的可靠性是这么体现的:在每收到一个正确的、符合次序的片段之后,就向发送方(也就是连接的另一段)发送一个特殊的TCP片段,回复一个ACK给发送方(ACK,acknowledge):“我已经收到那个片段了。”
- 这个特殊的TCP片段叫做ACK回复。如果一个片段序号为L,对应ACK回复有回复号L+1,也就是接收方期待接收的下一个发送片段的序号。如果发送方在一定时间等待之后,还是没有收到ACK回复,那么它推断之前发送的片段一定发生了异常。发送方会重复发送(retransmit)那个出现异常的片段,等待ACK回复,如果还没有收到,那么再重复发送原片段… 直到收到该片段对应的ACK回复(回复号为L+1的ACK)。
- 当发送方收到ACK回复时,它看到里面的回复号为L+1,也就是发送方下一个应该发送的TCP片段序号。发送方推断出之前的片段已经被正确的接收,随后发出L+1号片段。ACK回复也有可能丢失。对于发送方来说,这和接收方拒绝发送ACK回复是一样的。发送方会重复发送,而接收方接收到已接收过的片段,推断出ACK回复丢失,会重新发送ACK回复。
通过ACK回复和重新发送机制,TCP协议将片段传输变得可靠。但我们也看出来了—— 这也太麻烦了吧!
而TCP 的连接是“可靠的”,这一点还体现在 滑动窗口机制和重传机制上。
TCP 滑动窗口机制:
滑窗:
上面的工作方式中,发送方保持发送->等待ACK->发送->等待ACK…的单线工作方式,这样的工作方式叫做stop-and-wait。stop-and-wait虽然实现了TCP通信的可靠性,但同时牺牲了网络通信的效率。在等待ACK的时间段内,我们的网络都处于闲置(idle)状态。我们希望有一种方式,可以同时发送出多个片段。然而如果同时发出多个片段,那么由于IP包传送是无次序的,有可能会生成乱序片段。
在stop-and-wait的工作方式下,乱序片段完全被拒绝(序列号对不上,会等待重发),这也很不效率。毕竟,乱序片段只是提前到达的片段。我们可以在缓存中先存放它,等到它之前的片段补充完毕,再将追加在后面。
然而,如果一个乱序片段实在是太过提前(太“乱”了),该片段将长时间占用缓存。我们需要一种折中的方法来解决该问题:利用缓存保留一些“不那么乱”的片段,期望能在段时间内补充上之前的片段(暂不处理,但发送相应的ACK);对于“乱”的比较厉害的片段,则将它们拒绝(不处理,也不发送对应的ACK)。
这个机制就是——窗口机制
窗口机制
TCP滑动窗口技术可以通过动态改变窗口大小来实现对端到端设备之间的数据传输进行流量控制。
滑窗(sliding window)被同时应用于接收方和发送方,以解决以上问题。发送方和接收方各有一个滑窗。当片段位于滑窗中时,表示TCP正在处理该片段。滑窗中可以有多个片段,也就是可以同时处理多个片段。滑窗越大,越大的滑窗同时处理的片段数目越多(当然,计算机也必须分配出更多的缓存供滑窗使用)。
流量控制
主机和服务器之间可以通过滑动窗口来实现流量控制。
TCP协议会根据情况自动改变滑窗大小,以实现流量控制。流量控制(flow control)是指接收方将 window的大小通知给发送方,从而指导发送方修改发送 window的大小。接收方将该信息放在TCP头部的window size区域:发送方在收到window size的通知时,会调整自己滑窗的大小。这样,发送窗口变小,文本流发送速率降低,从而减少了接收方的负担。
TCP重传机制:
超时重传机制
超时重传机制是指:当发送方送出一个TCP片段后,将开始计时,等待该TCP片段的ACK回复。如果接收方正确接收到符合次序的片段,接收方会利用ACK片段回复发送方。发送方得到ACK回复后,继续移动窗口,发送接下来的TCP片段。如果直到计时完成,发送方还是没有收到ACK回复,那么发送方推断之前发送的TCP片段丢失,因此重新发送之前的TCP片段。这个计时等待的时间叫做重新发送超时时间(RTO, retransmission timeout)。
快速重传机制
快速发送机制如果被启动,将打断计时器的等待,直接重新发送TCP片段。
快速重传机制:实现了另外的一种丢包评定标准,即如果连续收到3次 ACK,发送方就认为这个seq的包丢失了,立刻进行重传,这样如果接收端回复及时的话,基本就是在重传定时器到期之前,提高了重传的效率。
TCP的可靠机制虽然保证了数据安全,但是往往也带来某些方面的缺陷:例如TCP慢启动和TCP全局同步现象。
TCP的缺陷:
一个概念——慢启动:
TCP连接刚建立时,不是一下子就能把握TCP窗口值大小的,会从一个小的窗口值慢慢往上加,最后协商成功,达到稳定的windows大小。
TCP全局同步:
当网络发生堵塞时,报文就不能及时送达目的端。对于TCP报文,如果大量的报文被丢弃,将造成TCP超时,从而引发TCP慢启动,使得TCP减少报文的发送。当队列同时丢弃多个TCP连接的报文时,将造成多个TCP连接同时进入拥塞避免和慢启动状态以调整并降低流量,这就被称为TCP全局同步现象。这样多个TCP连接发往队列的报文将同时减少,而后又会在某个时间同时出现流量高峰,如此反复,使网络资源利用率低。
资源消耗:
TCP 最大的缺点是那些”确认码”数据包把数量翻了一倍,但并没有传输更多信息。有时候这种代价是不值得的,于是有了UDP这个协议来代替一些用户的其他需求(下一篇我们会讲到)
这一章节我不知不觉就写了四千字(捂脸),可见TCP之重要性… 刚开始学TCP的时候,我还没有意识到问题的严重性…hhh
这几天写的博客有点多,因为热爱,所以无惧! 当回过头来看这些文章时,更多的是一种自豪感,我会继续坚持下去的。Fighting!