shuguang's blog

环境决定基础,选择决定价值,努力决定方向。

网络协议 - 传输层

  • 本文主要介绍传输层的两个协议TCPUDP数据格式,并将两者进行了比较。

  • 本文也重点介绍了TCP的可靠传输(超时重传)、流量控制(点到点)、拥塞控制 (共同维护)、连接管理(建立连接、释放连接)四个方面技术。


#传输层的两个主要协议

  • TCP(Transmission Control Protocol)传输控制协议(不丢包

  • UDP(User Datagram Protocol)用户数据报协议(丢包,实时(时间同步))

    TCP和UDP协议对比


#UDP协议

  • 数据格式

    • UDP无连接的,减少了建立和释放连接的开销

    • UDP尽最大的能力交付,不保证可靠交付,因此不需要维护一些复杂的数据,首部只有8个字节(TCP首部至少需要20 字节

    • UDP格式(首部 + 数据)
      UDP格式

    • 注:

      • 其中UDP的长度为16位,为: 首部的长度 + 数据的长度
      • UDP的数据长度字段是冗余的,因为完全可以通过IP数据包的首部长度和总长度计算出来传输层的数据长度。
      • 传输层的数据长度 = 网络层的总长度 - 网络层的首部长度 - 传输层的首部长度

  • 检验和(Checksum)

    • 检验和的计算内容:伪首部 + 首部 + 数据
    • 伪首部:占12字节
    • 伪首部: 仅在计算首部检验和的时候起作用, 并不会将伪首部传递分网络层。(增强检验功能)

  • 相关补充 - 常用命令、端口(Port)

    • UDP首部中端口是占用2字节,端口的取值范围: 0-65535

    • 在请求的过程中客户发送数据的端口是随机开启的端口

    • 可以通过防火墙设置开启或关闭某些端口来提高安全性。(例如:服务器关闭Mysql数据库3306端口, 不允许外部访问。)

    • 常用端口:
      不同协议默认端口号

      • netstat –an:查看被占用的端口

      • netstat –anb:查看被占用的端口、占用端口的应用程序

      • telnet 主机 端口:查看是否可以访问主机的某个端口

      • 安装telnet:控制面板 – 程序 – 启用或关闭Windows功能 – 勾选“Telnet Client” – 确定


#TCP协议

  • TCP协议 - 数据格式、检验和、标志位(Flags)

    • 数据偏移(数据的偏移值或者是首部长度)

      • 占四位,取值范围是0x0101- 0x1111(5 - 15)
      • 乘以4:首部长度(Header Length)
      • 首部长度范围: 20到60字节
    • 检验和(Checksum)

      • 同UDP一样,仅在计算首部检验和的时候起作用, 并不会将伪首部传递分网络层
        UDP报文数据格式
    • 标志位(Flags)

      • URG(Urgent)
        • 当URG为1的时候,紧急指针数值有效.表明当前报文段中有紧急数据,应优先传送
      • ACK(Acknowledgment)
        • 当ACK为1 ,确认号字段才有效.
      • RST(Reset)
        • 当RST=1时, 表明连接中出现严重差错,应释放连接,然后再重新建立连接.
      • SYN(Synchronization)(同步)
        • 当SYN为1,ACK为0表明是建立连接请求
        • 同意建立连接的回复,回复为SYN为1,ACK为1
        • 对连接的回应时,回复为SYN为0,ACK为1
      • FIN (Finish)
        • 表明数据释放完毕,要求释放连接
      • PSH(Push)
    • 保留

      • 占6位,目前全为0
    • 补充小细节

      • 有些资料保留位(Reserved)是3位,标志位是6位,由于前3位标志位是不用的,所以两种说法都没问题,并且wireshark就是这样的
        TCP报文数据格式

  • TCP的几个要点

    1. 可靠传输 (超时重传)
    2. 流量控制(点到点)
    3. 拥塞控制 (共同维护)
    4. 连接管理(建立连接、释放连接)

  • TCP - 序号、确认号、窗口

    • 序号(Sequence Number)
      • 占四个字节
      • 在传输过程中的每一个字节(段)都有一个编号
      • 建立连接之后,这个编号代表这一次TCP数据部分的第一个字节的编号
    • 确认号
      • 占四个字节
      • 在建立连接之后,确认号代表,期望对方下一次传来的TCP数据部分的第一个字节编号
    • 窗口(Window)
      • 占两个字节(具体需要乘以窗口缩放系数)
      • 这个字段具有流量控制功能, 用以告诉下一次发送语序的而数据大小(单位为字节)

  • TCP可靠传输 - 停止等待ARQ协议

    • ARQ(Auto Repeat-reQuest), 自动重传请求(超时重传)
      可靠传输-ARQ协议

    • 连续ARP协议 + 滑动窗口协议(提高效率)

      • 发送方的窗口大小是由接受方的窗口的缓存大小决定的
        连续ARQ协议-滑动窗口协议

      连续ARQ协议-滑动窗口协议-详细流程

    • TCP可靠传输 - SACK(选择性确认技术 )

      • 背景

        • 在传输过程中,若发送的序列中间某个数据包丢失(1,2,3,4,5其中3丢失了)TCP会通过重传最后确认的分组后续的分组(若最后确认的是2,会重传3、4、5)
        • 这样原先已经正确传输的分组也可能重复发送(比如4、5),降低了传输性能
      • 选择性确认技术

        • 告诉发送方哪些数据丢失,哪些数据已经提前收到
        • 使TCP只重新发送丢失的包(比如3),不用发送后续所有的分组(比如4、5)
          丢包过程
      • SACK信息会放在TCP首部的选项部分(当Kind=5,代表这是SACK选项)

        • Kind:占1字节。值为5代表这是SACK选项
        • Length:占1字节。表明SACK选项一共占用多少字节
        • Left Edge:占4字节,左边界
        • Right Edge:占4字节,右边界
        • (左边界301 - 右边界401的范围就代表301 - 400)
          抓包中的SACK信息
      • 返回确认的过程中,会告诉窗口缓存大小

        • 一对边界信息需要占用8字节,由于TCP首部的选项部分最多40字节,所以
          • SACK选项最多携带4组边界信息
          • SACK选项的最大占用字节数 = 4 * 8 + 2 = 34

      • 相关补充

        • 一个包多次重传失败
          • 不会一直重传知道成功为止,这个取决于操作系统的设置(有些系统,重传5次未成功(未收到确认报文)就会发送reset报文(RST)断开TCP连接,然后重新建立连接。
        • 不足接收窗口大小
          • 接收方会等待一定时间后没有后续包,就会返回确人接收到的包
        • 在传输层分片(而不是在网络层)
          • 提高传输的性能(可以(在传输层)发送指定分片丢失的包)
          • 可靠传输是在传输层中进行控制的,如果不分段,一旦出现数据丢失,整个传输层数据都得重传(SACK)
          • 需要明确的是可靠传输是在传输层进行控制。

  • TCP - 流量控制(端到端)

    • 背景

      • 接受方的缓存区大小是有限的(灵活调整的),发送方还发送数据,那么接受方就丢掉数据,这样会造成网络堵塞,所以需要流量控制(控制发送速率)。
    • 原理

      • 通过确认报文中的窗口字段来控制发送方的发送速率
      • 发送方发送的额窗口大小不能大于接受方给出的窗口大小(接受窗口大小为0,发送方就会停止发送数据)
    • 特殊情况

      • 一开始,接受方给发送方发送的窗口为0的报文段,但是后面又有了一些空间,但是给发送方发送的非0窗口的确认报文段丢失了,于是双方陷入僵局。
      • 解决方案
        • 发送方接受到0窗口大小的报文就会停止发送报文,并且会开启一个计时器,隔一段时间发送一个测试报文询问接受方 最新窗口大小,窗口大小为0就会继续开启计时器。

  • TCP - 拥塞控制

    • 简介

      • 背景:
        • 拥塞控制是为了防止过多的数据注入网络,从而避免网络中的路由器或链路过载,产生的,拥塞控制是全局性的,涉及降低网络传输性能有关的所有因素。
          网络拥塞丢包过程
    • TCP - 拥塞控制的方法

      • 慢开始(slow start,慢启动)

      • 拥塞避免(congestion avoidance)

      • 快速重传(fast retransmit)

      • 快速恢复(fast recovery)

      • MSS(Maximum Segment Size):每个段最大的数据部分大小(在建立连接时确定)(理论值)MTU一般是(1500) - 20 - 20 = 1460

      • cwnd(congestion window):拥塞窗口

      • rwnd(receive window):接收窗口(可发送所有数据的大小)

      • swnd(send window):发送窗口

      • swnd = min(cwnd, rwnd)

    • TCP -【拥塞控制】- 慢开始(slow start)
      慢开始

    • TCP -【拥塞控制】- 拥塞避免(congestion avoidance)

      拥塞避免

      • ssthresh (slow start threshold):慢开始阈值,cwnd达到阈值后,开始拥塞避免(加法增大)
      • 拥塞避免(加法增大):拥塞窗口cwnd 缓慢增大,以防止网络过早出现拥塞
      • 乘法减小:只要出现网络拥塞,把ssthresh减为拥塞峰值的一半,同时执行慢开始算法(cwnd又恢复到初始值)
      • 当网络出现频繁拥塞时,ssthresh值就下降的很快

    • TCP -【拥塞控制】- 快重传、快恢复

      • 快重传:

        • 接收方:
          • 每接收到一个失序的分组就立即发送重复确认
          • 使发送方能快速确认有分组没有到达
          • 不需要等待自己再发送数据,再确认哪些数据丢失了(SACK)
        • 发送方:
          • 只要发送方连续接受到3个连续的确认(总共4个),就应当立即重传对方尚未接收到的报文段
          • 不必继续等待重传计时器到期后再重传
            快重传

      • 快恢复:

        • 当发送方连续收到三个重复确认,说明网络出现拥塞
        • 就执行 “乘法减小” 算法,把ssthresh(slow start threshold)减为拥塞峰值的一半
        • 与慢开始不同之处是现在不执行慢开始算法,即cwnd现在不恢复到初始值
        • 而是把cwnd值设置为新的ssthresh值(减小后的值)
        • 然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大

      • 快重传 + 快恢复
        快重传+快恢复

        • 发送窗口的最大值swnd = min(接收窗口cwnd, 堵塞窗口rwnd)
          • 当 rwnd < cwnd 时,是接收方的接收能力限制发送窗口的最大值
          • 当 cwnd < rwnd 时,则是网络的拥塞限制发送窗口的最大值

  • TCP的连接管理

    • TCP - 序号和确认号(详细步骤)

      图1

      图2

      图3

      图4

      图5

    • TCP - 【建立连接】- 3次握手

      • 状态解读:
        TCP-3次握手
      • 状态
        • CLOSED:client处于关闭状态
        • LISTEN:server处于监听状态,等待client连接
        • SYN-RCVD:表示server接受到了SYN报文,当收到client的ACK报文后,它会进入到 ESTABLISHED 状态
        • SYN-SENT:表示client已发送SYN报文,等待server的第2次握手ESTABLISHED:表示连接已经建立
      • 前2次握手的相同点
        • YN 都设置为1
        • 数据部分的长度都为0
        • TCP头部的长度一般是32字节
          • 固定头部:20字节
          • 选项部分:12字节
        • 双方会交换确认一些信息
          • 比如MSS是否支持SACKWindow scale(窗口缩放系数)
          • 这些数据都放在了TCP头部的选项部分中(12字节)
      • 采用三次握手原因
        • 主要目的防止server端一直等待,浪费资源
        • 当client发出的第一个连接请求报文段因为网络延迟(并且重发),
        • 后续请求在服务器连接释放以后的某个时间才到达server,导致server端认为想建立新连接,并且服务器返回确认给客户端
        • client对server确认不理睬
        • 进而会导致server一直等待,浪费资源。
      • 第三次握手失败处理
        • 此时server的状态为 SYN-RCVD,若等不到client的 ACK,server会重新发送 SYN+ACK 包
        • 如果server多次重发 SYN+ACK 都等不到client的 ACK(也就是超时),就会发送 RST包强制关闭连接(被杀死)
        • 如果收到其他报文段或报文,就会发出差错报文。
    • TCP - 【释放连接】- 4次挥手

      • 状态解读
        TCP-4次挥手
      • FIN-WAIT-1:表示想主动关闭连接
        • 向对方发送了FIN报文,此时进入到FIN-WAIT-1状态
      • CLOSE-WAIT:表示在等待关闭
        • 当对方发送FIN给自己,自己会回应一个ACK报文给对方,此时则进入到CLOSE-WAIT状态
        • 在此状态下,需要考虑自己是否还有数据要发送给对方,如果没有,发送FIN报文给对方
      • FIN-WAIT-2:只要对方发送ACK确认后,主动方就会处于FIN-WAIT-2状态,然后等待对方发送FIN报文
      • CLOSING:一种比较罕见的例外状态
        • 表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN 报文
        • 如果双方几乎在同时准备关闭连接的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态(不会进入FIN-WAIT-2状态,最终进入TIME-WAIT状态)
        • 表示双方都正在关闭连接
      • LAST-ACK:被动关闭一方在发送FIN报文后,最后等待对方的ACK报文
        • 当收到ACK报文后,即可进入CLOSED状态了
      • TIME-WAIT:表示收到了对方的FIN报文,并发送出了ACK报文,就等 2MSL 后即可进入CLOSED状态了
        • 如果FIN-WAIT-1状态下,收到了对方同时带FIN标志和ACK标志的报文时
        • 可以直接进入到TIME-WAIT状态,而无须经过FIN-WAIT-2状态
      • CLOSED:关闭状态
        • 由于有些状态的时间比较短暂,所以很难用 netstat 命令看到,比如SYN-RCVD、FIN-WAIT-1等
      • 归纳
        • 前两次是关闭了客户端到服务器的发送通道,后两次是关闭了服务器到客户端的发送通道。
    • 相关补充

      • TCP / IP 协议栈在设计上,允许任何一方先发起断开连接。
      • 一般是等待2倍的 MSL(Maximum Segment Lifetime,最大分段生存期)(MSL是TCP报文段在Internet上的最长生存时间)
        • 每个具体的TCP实现都必须选择一个确定的MSL值,RFC 1122建议是2分钟,可以防止本次连接中产生的数据包误传到下一次连接中(因为本次连接中的数据包都会在2MSL时间内消失了)
      • 为什么会在Client发送确认Server的ACK后,需要有个TIME-WAIT阶段,等待一段时间后,然后再真正关闭连接?
        • 如果客户端在发送完ACK后直接进入CLOSED 状态,中间可能因为网络原因,server丢失了client的ACK,就会重发FIN来释放连接也就会引发以下的两种状况
          • 客户端关闭了,没有任何反应,服务器就会等很久,甚至多次发送FIN报文,浪费资源。
          • 客户端刚好打开一个应用程序分配和刚刚那个同一个端口,新的应用程序就会收到FIN,执行关闭连接的操作(本来是需要建立来连接的)
      • 四次挥手的原因
        1. TCP是全双工模式的,双方同时可以发送和接收数据。
        2. 第一次挥手,表示主机1告诉主机2没有数据需要发送,此时主机1仍可以接收主机2的数据。
        3. 第二次挥手,表示主机2知道主机1没有数据需要发送,此时主机2仍可以发送数据到主机1。
        4. 第三次挥手,表示主机2告诉主机1没有数据需要发送
        5. 第四次挥手,表示主机1已经知道主机2没有数据发送了。随后正式断开整个TCP连接
        6. 有时通过抓包可以发现,又是后只有三次挥手,其实是将第二和第0三次挥手合并了,在接收到FIN的同时,通知没有数据需要发送了。
      • 长连接和短连接(建立连接的目的)
        • 长连接:客户端建立完连接,只需要拿完数据不断开(频繁数据交互)就是长连接。
        • 短连接:客户端建立完连接,只需要拿完数据后立刻断开连接