一. TCP协议
TCP(Transmission Control Protocol), 面向连接的, 可靠的, 基于字节流的传输层控制协议
TCP首部数据格式
端口号: 源端口号和目的端口号分别占用2字节, 端口号主要为了识别发送端和接收端的进程
序号: TCP是面向字节流的, 所以在传输的字节流数据都是按照顺序进行编号, 序号的值是指本次发送的数据第一个字节的序号
当一个数据的序号为101, 而这个数据大小为100B, 则该数据最后一个字节的序号为200, 所以下一个报文段的数据序号应该从201开始.
确认号: 是期望收到对方下一个报文段的第一个数据字节的序号, 同时也表明序号N - 1之前的所有数据全都已正确收到
B已正确收到A发送过来的一个序号为101的报文段, 数据长度为100B, 表明B已经收到了序号200为止的所有数据, 因此B期望收到A的下一个数据序号为201, 所以B给A发送的确认报文段中的确认号为201
数据偏移: 即首部长度, TCP除了固定的20B的首部长度之外, 还有数据和选项字段, 所以还需要数据偏移位指定TCP数据报首部至数据部分的长度. 因为TCP数据报的长度为4B的整数倍, 没有数据的部分填充0, 数据报的首部一共为15*4B=60字节.
保留: 备用, 设置为0
标志
- URG: 紧急指针字段, 表明该数据报是紧急字段, 具有高优先级, 不用在发送端缓存队列排队,配合紧急指针使用。
- ACK: 确认字段, 建立连接之后, 所有传送的数据都需要把ACK置为1
- RSH: 推送功能, 表明此数据报应该尽快交付给接收端的应用程序,不用等缓存填满之后再向上交付。
- RST: 重置连接, 表明此时连接出现错误, 需要重新连接
- SYN: 同步序号, 建立连接
- FIN: 来自发送方的最后一个数据包, 关闭连接
窗口大小: 接收窗口的大小, 表示该段的发送者当前接收的窗口大小单位
校验和: 对TCP报头, 有效负载和IP伪报头进行错误检查. 伪报头由源 IP 地址、目标 IP 地址、TCP 协议的协议号 以及 TCP 报头和有效负载的长度(以字节为单位)组成。
紧急指针: 如果设置了 URG 标志,紧急数据的末尾, 紧急数据是指数据报头到紧急指针中间的部分
选项: 最大报文长度, TCP报文段中的数据字段的最大长度.
填充: 为了使整个首部长度是4B的整数倍.
二.连接管理
1. 三次握手
在客户端与服务器建立连接之前, 服务器必须首先绑定并监听一个端口之后才能进行连接, 这称为被动打开.这时客户端可以通过三次握手启动主动打开来建立连接.
(1) 客户端向服务器建立连接, 此时SYN置为1, 并且设置一个随机的序列号(seq=x)发送给服务器, 此时进入SYN_SENT状态
(2) 客户端收到客户端请求, 需要确认客户端的SYN, 将确认号置为x+1, 同时自己也发送一个随机的序列号(seq=y), 此时服务器进入SYN_RECV状态
(3) 客户端收到服务器的SYN+ACK包, 向服务器发送确认包ACK(ack=y+1), 客户端和服务端进入ESTABLISHED状态
2. 四次挥手
(1) 客户端数据发送完毕, 请求释放连接. FIN=1, seq=u, 客户端进入FIN_WAIT_1状态
(2) 服务器接收到连接终止信号之后, 需要进行确认, 此时ack=u+1, 并且发送一个seq=v, 服务器进入CLOSE_WAIT状态, 客户端收到响应后, 进入FIN_WAIT_2状态
(3) 服务器在结束所有数据传输之后, 将FIN置为1, 请求释放连接, 发送一个序列号seq=w, 进入LAST_ACK状态
(4) 客户端将ACK置为1, 确认号ack=w+1,响应给服务器,客户端进入TIME_WAIT状态,等待2MSL(最长报文寿命)后, 进入CLOSED状态, 服务器收到后, 进入CLOSED状态
3. 超时重传
发送方发送一个段时, 它会使用对确认到达时间的保守估计来初始化一个计时器。如果计时器到期,则重新传输该段,新的超时阈值是先前值的两倍,从而导致指数退避行为。
4. 冗余ACK重传
冗余ACK是发送方之前已经收到过该报文段的确认, 并且当前再次确认这个报文段的ACK 当发送方收到3次同一个报文段的冗余ACK时, 就认为在这个被确认报文段之后的报文段已经丢失
三.流量控制
1.滑动窗口
TCP提供流量控制服务用来避免发送方由于发送速率太快使接收方缓存区溢出的可能, 因此可以说流量控制是一个速度匹配服务, 匹配发送方的发送速率和接收方的读取速率
TCP利用滑动窗口机制, 实现流量控制
发送方发送数据时, 由于可能数据比较大, 所以需要拆分成多个数据包进行发送, TCP协议需要对数据进行确认, 发送方才能继续发送下一个数据包. 上图每每发送一个数据包, 都需要得到接收端的确认应答之后才能继续发送, 这样一来, 就会在等待确认应答包环节浪费时间, 为了避免这种情况, 所以就有了窗口的概念.
当发送方发送第一个数据包1-1000时, 并不需要收到接收方的确认应答, 而是继续发送第二(1001-2000)和第三个(2001-3000)数据包, 当三个数据包发送完成之后, 并且接收到三个数据包的确认应答时则继续发送数据包. 这种方式可以省去多个数据包的确认应答包时间, 从而避免了吞吐量的降低
利用窗口滑动机制, 向后移动, 确保下一次发送仍然可以发送窗口大小的数据包.
在发送过程中接收端响应的确认应答包可能并不是发送端发送过来的最后数据包. 发送端发送2001-5000的一个数据包, 接收端响应的确认应答包为下一个发送4001, 表示接收端成功响应了前两个数据包, 没有响应最后一个数据包, 那么最后一个数据包要保留在窗口中. 由于窗口大小为3, 发送端除了最后一个包以外, 还可以继续发送两个数据包(5001-6000和6001-7000)
2.数据重发
在进行数据包传输时,难免会出现数据丢失情况。这种情况一般分为两种。
第一种,如果未使用滑动窗口机制,发送的数据包没有收到确认应答包,那么数据都会被重发;如果使用了滑动窗口机制,即使确认应答包丢失,也不会导致数据包重发。 第二种,发送的数据包丢失,将导致数据包重发。
(1) 确认应答包丢失
-
发送方第一次发送数据包 当前窗口大小设为3, 说明最大发送数据包为3, 发送方发送三个数据 -
接收方均已收到发送方的数据, 响应发送方发送确认应答 -
发送方第二次发送数据包, 确认应答2001丢失, 但是最后一个数据包的确认应答没有丢失, 此时接收方认为发送的三个数据包(1-1000, 1001-2000, 2001-3000)均已被接收端收到, 所以不重发数据. 继续发送(3001-4000, 4001-5000, 5001-6000)数据包 -
接收方均已收到发送方第二次发送的数据包, 响应发送方法发送确认应答 -
发送方只收到第二次发送的三个数据包最后一个确认应答, 说明第二次发送的数据都没有丢失, 所以继续发送(6001-7000, 7001-8000, 8001-9000)数据包
(2) 数据包丢失
-
发送方第一次发送数据包, 此时窗口大小设为4, 发送(1-1000, 1001-2000, 2001-3000,3001-4000)数据包 -
接收方接收数据, 发现2001-3000数据包丢失, 则接收方需要获取2001-3000数据包, 返回确认应答2001, 此时发送方又发送3001-4000数据包, 然而该数据包并不是接收到期望收到的数据包, 则再次返回确认应答2001 -
发送方第二次发送数据包(4001-5000, 5001-6000, 6001-7000,7001-8000) -
接受方接收到数据(4001-5000)发现, 也不是期望收到的数据包, 并且返回确认应答2001, 再次接收数据(5001-6000), 返回确认应答2001 -
发送方接收到三次重复的确认应答时, 认为该数据包(2001-3000)已经丢失, 会进行重发该数据包 -
收到数据包(2001-3000), 然后告诉发送端, 下一个应该发送8001的数据包 -
发送端发送数据包(8-9000, 9001-10000, 10001-11000,11001-12000)
3. TCP窗口控制
在使用滑动窗口机制进行传输的时候, 需要根据实际来发送数据包
(1) 如果窗口过小, 发送端发送少量的数据包, 接收端处理的很快, 并且还能处理跟多的数据包, 当传输较大的数据时需要等待发送方造成很大的延迟
(2) 如果窗口过大, 发送方发送大量的数据包, 而接收端处理不了这么多的数据包, 就会堵塞链路。如果丢弃这些本应该接收的数据包,又会触发重发机制。
为了避免这种现象的发生,TCP 提供了流控制。所谓的流控制就是使用不同的窗口大小发送数据包。发送端第一次以窗口大小(该窗口大小是根据链路带宽的大小来决定的)发送数据包,接收端接收这些数据包,并返回确认应答包,告诉发送端自己下次希望收到的数据包是多少(新的窗口大小),发送端收到确认应答包以后,将以该窗口大小进行发送数据包。
注意: 如果接收端返回确认应答中, 窗口设置为0, 则表示现在不能接收任何数据, 只有到接收端发送窗口通知才可以继续发送数据包 假如这个窗口通知丢失了, 可能会造成无法继续通信, 这时发送方时不时发送窗口探测包, 这个包有1个字节, 用来获取最新的窗口大小的信息.
|