已tcp/ip4层协议划分,TCP处于传输层。 ![[Pasted image 20231115145615.png]] ## TCP的特点 + 面向连接的传输层协议,应用程序在使用 TCP 协议之前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接。 + 每一条TCP连接只能有2个端点,每一条TCP连接只能是点对点。 + 提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复,并且**按序**到达。 + 提供全双工通信,TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据。 + 面向字节流,“流”指的是`流入到进程或从进程流出的字节序列`。 ## 一、面向字节流 虽然应用程序和 TCP 的交互式一次一个数据块(大小不等),但 TCP 把应用程序交下来的数据仅仅看成是一连串的`无结构的字节流`。TCP 并不知道所传送的字节流的含义。 TCP 不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系。 接收方应用程序收到的字节流必须和发送方应用程序发出的字节流完全一样。接收方的应用程序必须有能力识别收到的字节流,把它还原成有意义的应用层数据。 ![[Pasted image 20231115152917.png]] TCP 和 UDP 在发送报文时采用的方式完全不同。TCP 并不关心应用进程一次把多长的报文发送到 TCP 的缓存中,而是根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应包含多少个字节(UDP 发送的报文长度是应用进程给出的)。如果应用进程传送到 TCP 缓存的数据块太长,TCP 就可以把它划分短一些再传送。如果应用进程一次只发来一个字节,TCP 也可以等待积累有足够多的字节后再构成报文段发送出去。(TCP缓存) ## 二、TCP连接 tcp 协议将连接作为最基本的抽象,tcp连接的端点叫做套接字或插口。端口号拼接IP地址即构成套接字,`socket=IP:PORT` 每一条TCP连接唯一地被通信两端的两个端点(两个套接字)所确定 `TCP连接={socket1,socket2}` TCP 连接就是由协议软件所提供的一种抽象,**同一个 IP 地址可以有多个不同的 TCP 连接,而同一个端口号也可以出现在多个不同的 TCP 连接中。 ## 三、可靠传输的原理 tcp使用ARQ(自动重传请求)协议实现可靠传输。 ![[Pasted image 20231115161031.png]] 只要超过一段时间没有收到确认,就认为刚才发送的分组丢失了,因而重传前面发送过的分组。这就叫做`超时重传`。要实现超时重传,就要在每发送完一个分组时设置一个`超时计时器` - 发送完一个分组后,`必须暂时保留已发送的分组的副本`(在发生超时重传时使用)。只有在收到相应的确认后才能清除暂时保留的分组副本 - 分组和确认分组都必须进行`编号`。这样才能明确是哪一个发送出去的分组收到了确认,而哪一个分组还没有收到确认 - 超时计时器的重传时间`应当比数据在分组传输的平均往返时间更长一些` ## 四、报文 TCP报文分为头部和数据2个部分,TCP 报文段首部的前20个字节是固定的,后面有4n字节是根据需要而增加的选项(n是整数)。因此 TCP 首部的最小长度是20字节。 ### 1.头部 ![[Pasted image 20231115161759.png]] - `源端口`和`目的端口` 各占2个字节,分别写入源端口号和目的端口号 - `序号` 占4字节。序号范围是[0, 232-1],共232(即4 294 967 296)个序号。序号增加到232-1后,下一个序号就又回到0。在一个 TCP 连接中传送的字节流中的`每一个字节都按顺序编号` - `确认号` 占4字节,是`期望收到对方下一个报文段的第一个数据字节的序号` - `数据偏移` 占4字节,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。这个字段实际上是指出 TCP 报文段的首部长度 - `保留` 占6位,保留为今后使用,但目前应置为0 6个控制位: - `紧急 URG(URGent)` 当 URG=1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据),而不是按原先的排队顺序来传送 - `确认 ACK(ACKnowledgment)` 仅当 ACK=1 时确认号字段才有效。当 ACK=0 时,确认号无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置1 - `推送 PSH(Push)` 当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能够收到对方的响应 - `复位 RST(ReSeT)` 当 RST=1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接 - `同步 SYN(SYNnchronization)` 在连接建立时用来同步序号。当 SYN=1 而 ACK=0 时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使 SYN=1 和 ACK=1 - `终止 FIN(FINis)` 用来释放一个连接。当 FIN=1 时,表明此报文段的发送发的数据已发送完毕,并要求释放运输连接 - `窗口` 占2字节。窗口值是[0, 216-1]之间的整数。窗口值作为接收方让发送方设置其发送窗口的依旧 - `检验和` 占2字节。检验和字段检验的范围包括首部和数据这两部分 - `紧急指针` 占2字节。紧急指针仅在 URG=1 时才有意义,它指出本报文段中的紧急数据的字节数 - `选项` 长度可变,最长可达40字节 ## 五、三次握手(建立连接) TCP 建立连接的过程叫做握手,握手需要在客户和服务器之间交换三个 TCP 报文段 ![[Pasted image 20231116103804.png]] 1. 最初客户/服务器的 TCP 进程都处于 `CLOSED(关闭)`状态。在本实例中,A `主动打开连接`,而 B `被动打开连接` 2. B 的 TCP 服务器进程先创建`传输控制块` TCB,并处于 `LISTEN(收听)` 状态,等待客户的连接请求 3. A 的 TCP 客户进程创建`传输控制模块` TCB。并向 B 发出连接请求报文段,首部中的同部位 SYN = 1,选择一个初始序号 seq = x。TCP 客户端进程进入 `SYN-SENT(同步已发送)` 状态。TCP 规定,SYN 报文段(即 SYN = 1 的报文段)不能携带数据,但要`消耗一个序号`(一次握手) 4. B 收到连接请求报文段后,如同意建立连接,则向 A 发送确认。在确认报文段中应把 SYN 位和 ACK 位都置1,确认号是 ack = x + 1,同时也为自己选择一个初始序号 seq = y。这时 TCP 服务器进程进入 `SYN-RCVD(同步收到)` 状态。这个报文段也不能携带数据,但同样`要消耗掉一个序号`(二次握手) 5. TCP 客户进程收到 B 的确认后,还要向 B 给出确认。确认报文段的 ACK 置1,确认号 ack = y + 1,而自己的序号 seq = x + 1。TCP 的标准规定,ACK 报文段可以携带数据。但`如果不携带数据则不消耗序号`,在这种情况下,下一个数据报文段的序号仍是 seq = x + 1。这时,TCP 连接已经建立,A 进入 `ESTABLISHED(已建立连接)` 状态(三次握手) 6. 当 B 收到 A 的确认后,也进入 `ESTABLISHED` 状态`传输控制块` TCB(Transmission Control Block)存储了每一个连接中的一些重要信息,如:TCP 连接表,指向发送和接收缓存的指针,指向重传队列的指针,当前的发送和接收序号等等。 **为什么 A 最后还要发送一次确认呢?这主要是为了防止已失效的连接请求报文段突然又传到了 B,因而产生错误 A 并没有发出建立连接的请求,因此不会理睬 B 的确认,也不会向 B 发送数据。但 B 却以为新的运输连接已经建立了,并一直等待 A 发来数据。B 的`许多资源就这样被浪费了`。采用三报文握手的办法,可以防止上述现象的发生。 ## 六、四次挥手(释放连接) ![[Pasted image 20231116112915.png]] 1. A 的应用进程先向其 TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP 连接。A 把连接释放报文段首部的终止控制位 FIN 置1,其序号 seq = u,它等于前面已传送过的数据的最后一个字节的序号加1。这时 A 进入 `FIN-WAIT-1(终止等待1)` 状态,等待 B 的确认。TCP 规定,FIN 报文段即使不携带数据,也消耗一个序号。 2. B 收到连接释放报文段后即发出确认,确认号是 ack = u + 1,而这个报文段自己的序号是 v,等于 B 前面已传送过的数据的最后一个字节的序号加1。B随即进入 `CLOSE-WAIT(关闭等待)` 状态。TCP 服务器进程这时应通知高层应用进程,因而从 A 到 B 这个方向的连接就释放了,这时的 TCP 连接处于 `半关闭(half-close)` 状态,即 A 已经没有数据要发送了,但 B 若发送数据,A 仍要接收。也就是说,从 B 到 A 这个方向的连接并未关闭,这个状态可能会持续一段时间 3. A 收到来自 B 的确认后,就进入`FIN-WAIT-2(终止等待2)` 状态,等待 B 发出的连接释放报文段 4. 若 B 已经没有要向 A 发送的数据,其应用进程就通知 TCP 释放连接。这时 B 发出的连接释放报文段必须使 FIN = 1。现假定 B 的序号为 w(在半关闭状态 B 可能又发送了一些数据)。B 还必须重复上次已发送过的确认号 ack = u + 1。这时 B 就进入 `LAST-ACK(最后确认)`状态,等待 A 的确认 5. A 在收到 B 的连接释放报文段后,必须对此发出确认。在确认报文段中把 ACK 置1,确认号 ack = w + 1,而自己的序号是 seq = u + 1(根据 TCP 标准,前面发送过的 FIN 报文段要消耗一个序号)。然后进入到 `TIME-WAIT(时间等待)`状态。此时 TCP 连接还没有释放掉。必须经过`时间等待计时器(TIME-WAIT timer)`设置的时间2MSL后,A 才进入到 `CLOSED` 状态 6. 当 A 撤销相应的传输控制块 TCB 后,就结束了这次的 TCP 连接时间 MSL 叫做`最长报文段寿命`(Maximum Segment Lifetime),RFC 793建议设为2分钟。但这完全是从工程上来考虑的,对于现在的网络,MSL = 2分钟可能太长了一些 TCP客户端和服务端之间关闭需要4次挥手,前两次的作用是关闭A->B的连接,此时B->A还处于半关闭状态,因此还需要2次挥手关闭B->A ## 七、TCP的有限状态机 为了更清晰地看出 TCP 连接的各种状态之间的关系,下图为 TCP 的有限状态机。图中每一个方框即 TCP 可能具有的状态。每个方框中的大写英文字符串是 TCP 标准所使用的 TCP 连接状态名。状态之间的箭头表示可能发生的状态变迁。箭头旁边的字,表明引起这种变迁的原因,或表明发生状态变迁后又出现什么动作。请注意图中有三种不同的箭头。`粗实线箭头`表示对`客户进程的正常变迁`。`粗虚线箭头`表示对`服务器进程的正常变迁`。另一种`细线箭头`表示`异常变迁` ![[Pasted image 20231116114425.png]]