# TCP

TCP(Transmission Control Protocol),又叫传输控制协议。 TCP 协议是面向连接的,可靠的,基于字节流的传输协议。

在基于 TCP 进行通信时,通信双方需要先建立一个 TCP 连接,建立连接需要经过三次握手,断开连接的时候需要经过四次挥手。

# 1. TCP 头部

TCP协议头部,固定20个字节,UDP头部只有8个字节,IP协议头部20个字节

TCP header

TCP header

对于 TCP 头部来说,以下几个字段是很重要的:

  • 序列号Sequence number
    • 0~ 2^32-1
    • 一个字节一个序号
    • 数据首字节序号(第一个字节)
    • 这个序号保证了 TCP 传输的报文都是有序的,对端可以通过序号顺序的拼接报文
  • 确认号Acknowledgement Number
    • 这个序号表示数据接收端期望接收的下一个字节的编号是多少,同时也表示上一个序号的数据已经收到
    • 确认号为N:表示N-1序号的数据都已经收到,比如,收到了序号为501的数据报,长度是100,下一次确认号则为601
  • 窗口大小 (Window Size)
    • 表示还能接收多少字节的数据,用于流量控制
  • 数据偏移
    • 占4位:0~15,单位为:32位字(由此可以看出最大偏移为15*4,即TCP首部长度介于20-60个字节之间)
    • 数据偏离首部的距离
    • 不知道TCP选项有多长,所有用数据偏移表示真实的数据离头部偏移有多少
  • 标识符
    • ACK=1 :该字段为一表示确认号字段有效。此外,TCP 还规定在连接建立后传送的所有报文段都必须把 ACK 置为一。
    • SYN=1:当 SYN=1,ACK=0 时,表示当前报文段是一个连接请求报文。当 SYN=1,ACK=1 时,表示当前报文段是一个同意建立连接的应答报文。
    • FIN=1:该字段为一表示此报文段是一个释放连接的请求报文。
    • URG=1 : 该字段为一表示本数据报的数据部分包含紧急信息,是一个高优先级数据报文,此时紧急指针有效。紧急数据一定位于当前数据包数据部分的最前面,紧急指针标明了紧急数据的尾部。
    • PSH=1 :该字段为一表示接收端应该立即将数据 push 给应用层,而不是等到缓冲区满后再提交。
    • RST=1:该字段为一表示当前 TCP 连接出现严重问题,可能需要重新建立 TCP 连接,也可以用于拒绝非法的报文段和拒绝连接请求。
  • 紧急指针
    • 紧急数据(URG=1)
    • 指定紧急数据在报文的位置
  • TCP选项
    • 最多40字节(60-20)
    • 支持未来的拓展

TCP

# 2. 可靠传输的基本原理

停止等待协议:

  • [ ] 发送方等待接收方的确认消息,才发送新的信息
  • [ ] 最简单的可靠传输协议
  • [ ] 通过超时重传保证可靠传输
  • [ ] 对信道的利用效率不高

停止等待协议,无差错的情况: 停止等待协议

出差错的情况,超时重传,包括接收方没有收到发送方的消息: 停止等待协议

超时重传,发送方没有收到接收方的确认信息: 停止等待协议

超时重传,确认消息很久才收到停止等待协议

小结:

  • [ ] 发送的消息在路上丢失了
  • [ ] 确认的消息在路上丢失了
  • [ ] 确认的消息很久才到

停止等待协议通过超时重传保证可靠传输

超时定时器:

  • [ ] 每发送一个消息,都需要设置一个定时器

# 2.1. 连续ARQ协议

  • [ ] ARQ(Automatic Repeat reQuest)自动重传请求
  • [ ] 批量发送和确认
  • [ ] 滑动窗口和累计确认是其两个重要概念

滑动窗口,收到前面的确认消息,滑动窗口向前移动,把滑动窗口内的未发送消息发送出去:

滑动窗口

并不需要对每一个报文都确认,而采用累计确认的方法。如收到了5的确认消息,则认为1-5的消息都已经收到了,就把滑动窗口往前移动5格

滑动窗口

# 3. TCP协议的可靠传输

  • [ ] TCP的可靠传输基于连续ARQ协议
  • [ ] TCP滑动窗口以字节为单位

滑动窗口里面的7个字节都是可以发送的,左边是已经确认的字节序号,右边是不允许发送的字节序号,窗口内最左边是对方期待收到的下一个字节

窗口内又可分为已发送未确认可用窗口,由于没收到前面的确认所以不能往前移动:

滑动窗口

有可能窗口内都是已发送未确认,可用窗口=0

没有按序收到确认消息,即收到后面的确认消息,但是没收到前面的,超时后,会从前面开始重传,效率低:

滑动窗口

选择重传:

  • [ ] 选择性的重传某些消息,而不是重传所有消息
  • [ ] 选择重传需要指定需要重传的字节
  • [ ] 每一个字节都有唯一的32位序号

TCP选项最多40个字节(60-20),即最多10个序号,指定的是需要重传的边界,而不是字节,表明需要重传的一段范围 一段一段,如果里面存了1000和1500指的是需要重传1000~1500这一段数据

# 4. TCP协议的流量控制

  • 特有的功能(UDP和其他协议没有)
  • 流量控制指让发送方发送速率不要太快
  • 流量控制是使用滑动窗口来实现的(确认号是501的话,如果窗口是1000,表明接收方希望接收501-1501的数据

通过窗口大小控制对方发送速率:

滑动窗口

如果丢失了最后的确认窗口变大(不为0)的消息,就会导致死锁,发送方一直等到对方窗口变大,接收方一直等待对方发送消息

坚持定时器(解决死锁):

  • [ ] 当发送方接收到窗口为0的消息,则启动坚持定时器
  • [ ] 坚持定时器每隔一段时间发送一个窗口探测报文

这种死锁相当于情侣一方A一直等待对方B改变脾气,而B已经改变了,但联系不到A,坚持定时器是A每隔一段时间问一下B,你改变了没有。

# 5. TCP协议的拥塞控制

  • [ ] 一条数据链路经过非常多的设备
  • [ ] 数据链路中各个部分都有可能成为网络传输的瓶颈(导致拥塞)

注意:

  • [ ] 流量控制考虑点对点的通信量的控制
  • [ ] 拥塞控制考虑整个网络,是全局性的考虑
  • [ ] 报文超时则认为是拥塞(虽然不一定)

慢启动算法:

  • [ ] 由小到大逐渐发送数据量
  • [ ] 每收到一个报文确认,就加一(指数增长,1 2 4 8 16…)
  • [ ] 增长到慢启动阈值(ssthresh)后就不增长了

拥塞避免算法:

  • [ ] 维护一个拥塞窗口的变量
  • [ ] 只要网络不拥塞,就试探着把拥塞窗口调大(比如到了慢启动阈值16后,以后发送17、18、19…个报文)

二者结合,先进行慢启动算法,再进行拥塞避免算法:

TCP协议的拥塞控制

上述过程就像一个人贪婪的过程

# 6. TCP连接的三次握手

三次握手

简单的说:

  • 第一次握手
    • SYN = 1, seq(client) = x
    • 客户端向服务端发送连接请求报文段。该报文段中包含自身的数据通讯初始序号。请求发送后,客户端便进入 SYN-SENT 状态
  • 第二次握手
    • SYN = 1,ACK = 1,ack(确认序号) = x+1, seq(server) = y
    • 服务端收到连接请求报文段后,如果同意连接,则会发送一个应答,该应答中也会包含自身的数据通讯初始序号,发送完成后便进入 SYN-RECEIVED 状态
  • 第三次握手
    • ACK = 1,ack(确认序号) = y+1, seq(client) = x + 1
    • 客户端收到连接同意的应答后,还要向服务端发送一个确认报文。客户端发完这个报文段后便进入 ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时连接建立成功。

为什么发送方要发出第三个确认报文(为什么需要第三次握手)?

  • 防止已经失效的连接请求报文传送到对方,引起错误

举例:

  • [ ] 发送方第一次握手时发送很久没有收到对方应答,于是发送了第二封,第二封比第一封更早到达,第一次便是失效的请求报文
  • [ ] 如果两次握手就能建立起连接:同一个请求发送两次(第一次超时)就会建立起两个连接,引起错误
  • [ ] 本来这是一个早已失效的报文段,但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求

为什么需要第三次握手

虚线是假设两次握手就建立连接

# 7. TCP连接的四次挥手

比三次握手多出来的是第二次挥手,意思是我收到了,但是我现在还没传完,等会关闭

四次挥手

TCP 是全双工的,在断开连接时两端都需要发送 FIN 和 ACK。

  • 第一次挥手
    • 若客户端 A 认为数据发送完成,则它需要向服务端 B 发送连接释放请求。
  • 第二次挥手
    • B 收到连接释放请求后,会告诉应用层要释放 TCP 链接。然后会发送 ACK 包,并进入 CLOSE_WAIT 状态,表示 A 到 B 的连接已经释放,不接收 A 发的数据了。但是因为 TCP 连接时双向的,所以 B 仍旧可以发送数据给 A。
  • 第三次挥手
    • B 如果此时还有没发完的数据会继续发送,完毕后会向 A 发送连接释放请求,然后 B 便进入LAST-ACK状态。
    • PS:通过延迟确认的技术(通常有时间限制,否则对方会误认为需要重传),可以将第二次和第三次握手合并,延迟 ACK 包的发送。
  • 第四次挥手
    • A 收到释放请求后,向 B 发送确认应答,此时 A 进入 TIME-WAIT 状态。该状态会持续 2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃) 时间,若该时间段内没有 B 的重发请求的话,就进入 CLOSED 状态。当 B 收到确认应答后,也便进入 CLOSED 状态。

注意:

  • [ ] 主动关闭的一方状态变化为:建立状态、第一次等待(FIN-WAIT-1)、第二次等待(FIN-WAIT-2)、等待计时(TIME-WAIT)、关闭
  • [ ] 被动关闭的一方状态变化为:建立状态、关闭等待(CLOSE-WAIT)、最后确认(LAST-ACK)、关闭状态
  • [ ] 主动关闭的一方最后有等待计时状态

MSL(MAX Segment Lifetime):最长报文段寿命

  • [ ] MSL建议设置为2分钟

等待计时器

  • [ ] 等待计时器,最长等待时间2MSL
  • [ ] 等待过程中,不会释放端口,只有等到等待计时器结束后,才释放

为什么需要等待2MSL

  • [ ] 最后一个报文没有确认
  • [ ] 确保发送方的ACK可以到达接收方(第1个作用)
  • [ ] 2MSL时间内没有收到,则接收方会重发
  • [ ] 确保当前连接的所有报文都已经过期(第2个作用)

等待计时状态就像主动提分手的那个人等待对方确实放下了,nice person

# 7.1. 为什么建立连接是三次握手,关闭连接确是四次挥手呢?

建立连接的时候, 服务器在 LISTEN 状态下,收到建立连接请求的 SYN 报文后,把 ACKSYN 放在一个报文里发送给客户端。

关闭连接时,服务器收到对方的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了

# 8. TCP 小结

# 8.1. 为什么 TCP 这么复杂?

因为既要保证可靠性, 同时又要尽可能提高性能

# 8.2. 保证可靠性的机制

  • 校验和
  • 序列号(按序到达)
  • 确认应答
  • 超时重传
  • 连接管理
  • 流量控制
  • 拥塞控制

# 8.3. 提高性能的机制

  • 滑动窗口
  • 快速重传
  • 延迟应答
  • 捎带应答

# 8.4. 定时器

  • 超时重传定时器
  • 保活定时器
  • TIME_WAIT 定时器

# 8.5. TCP/UDP的区别?

TCP:

  • 面向连接(TCP三次握手)
  • 可靠的(每发送一个包都会等待对方确认)
  • 基于字节流(缓冲区,应用层发送来的数据分段再进行发送)

UDP:

  • 无连接
  • 不可靠
  • 面向报文(应用层发来的数据直接传出去,没有缓冲区)

# 8.6. TCP 对应的协议和 UDP 对应的协议

TCP对应的协议:

  1. FTP:定义了文件传输协议,使用21端口。
  2. Telnet:一种用于远程登陆的端口,使用23端口,用户可以以自己的身份远程连接到计算机上,可提供基于DOS模式下的通信服务。
  3. SMTP:邮件传送协议,用于发送邮件。服务器开放的是25号端口。
  4. POP3:它是和SMTP对应,POP3用于接收邮件。POP3协议所用的是110端口。
  5. HTTP:是从Web服务器传输超文本到本地浏览器的传送协议。

UDP对应的协议:

  1. DNS:用于域名解析服务,将域名地址转换为IP地址。DNS用的是53号端口。
  2. SNMP:简单网络管理协议,使用161号端口,是用来管理网络设备的。由于网络设备很多,无连接的服务就体现出其优势。
  3. TFTP(Trival File Transfer Protocal),简单文件传输协议,该协议在熟知端口69上使用UDP服务。