TCP/IP协议相关

TCP/IP协议族

由一系列网络协议所组成的一个网络分层模型,是Internet的核心协议并被广泛应用于局域网和互联网。

分层

为什么要分层?

  • 每层内部的设计可以自由改动,只需要修改替换对应分层即可
  • 设计变得简单,分层只需要考虑当前的任务,不需要在意其他分层情况
  • 网络的不稳定性

TCP分层

应用层(Application Layer)

向用户提供应用服务时通信的活动

相关协议:

  • HTTP协议
  • FTP协议-文件传输
  • DNS协议-域名解析

传输层(Transport Layer)

提供处于网络连接中的两台计算机之间的数据传输

相关协议:

  • TCP协议
  • UDP协议

网络层(Internet Layer)

处理网络上流动的数据包,规定如何将数据包传送给对方

相关协议:

  • IP协议

处理连接网络的硬件部分,例如网卡、光纤等

物理层

例如,网线啥的

如何通信传输?

TCP/IP通信传输

进行通信时,会按照分层顺序与对方进行通信。发送端数据从应用层向下走,接收端数据向应用层往上走。

如上图所示:

  • 客户端向服务端发起请求
    • 在应用层(HTTP协议)发送请求数据
    • 传输层(TCP协议)接收到数据,并进行分割打上标记转发给网络层
    • 网络层(IP协议),增加做为通信目的地的服务器MAC地址转发给链路层
    • 链路层驱动硬件进行传输。
  • 服务端接受客户端数据
    • 链路层接收到数据
    • 网络层继续向上转发数据到传输层
    • 传输层向上转发到应用层
    • 应用层接收到数据
    • 一次HTTP请求完成

TCP协议

TCP协议位于传输层,提供可靠的字节流服务

字节流服务:方便数据的传输,将大块数据分割成报文段的数据包进行管理。

TCP协议要点.png

特点

特点 具体描述
面向连接 使用TCP传输数据前,必须先建立TCP连接;传输完成后在释放连接
面向字节流 虽然应用程序和TCP的交互是一次一个数据块,但TCP只把数据看为一连串无结构的字节流。数据以流的形式传输
全双工通信 建立TCP连接后,通信双方都能发送数据
可靠 TCP首部格式TCP传输的数据:不丢失、无差错、不重复,按序到达

TCP首部格式

TCP首部格式结构

TCP首部格式

  • 源端口号

    表示发送端端口号,字段长16位

  • 目标端口号

    表示接收端端口号,字段长16位

  • 序列号

    指的是本报文段第一个字节的序列号,长32位。每发送一次数据,就累加一次该数据的字节流大小。

    序列号不会从0、1开始,而是以一个随机数作为初始值(ISN),通过SYN包发送给服务端。

    主要作用如下:

    • SYN报文中交换彼此的初始序列号(ISN)
    • 保证数据报按正确的顺序发送

    ISN:随机生成的一个初始值,在三次握手过程中双端互相交换。

    ISN基本是每4ms加一,溢出则回到0,使猜测ISN过程变得困难。

    以上操作是为了避免被攻击者预测到,防止攻击者伪造RST控制位,导致连接被强制关闭。

  • 确认号

    表示告知对方下一个期望接收的序列号,长度为32位。小于确认号的所有字节已被全部正常接收。

  • 首部长度

    表示TCP传输的数据部分应该从TCP包的第几位开始计算,等价于TCP首部的长度。剩下的都为数据长度

  • 保留

    该字段是为了以后扩展时使用,长度为4位。

  • 控制位/标记位

    用来控制连接的流程

    控制位结构

    • CWR:控制拥塞窗口
    • ECE:值为1时,通知通信对方当前网络有拥塞。
    • URG:值为1时,表示包中有需要紧急处理的数据。
    • ACK:确认应答的字段变为有效。TCP规定除了最初建立连接时的SYN包之外该值必须设置为1。
    • PSH:若值为1,告知通信对方收到的数据应该直接上传到上层的应用层;若值为0,不需要立即上传而是先进行缓存。
    • RST:值为1,表示TCP连接出现异常必须强制断开连接。
    • SYN:用于建立连接,值为1表示希望建立连接,并进行初始序列号(ISN)的设置
    • FIN:值为1,表示今后不会再有数据发送,希望断开连接。
  • 窗口大小
    长度为16位,用于通知从相同TCP首部的确认号所指位置开始能接收的数据大小。告知对方允许发送的数据量。

    实际上该大小是不够用的。因此TCP支持窗口缩放选项,作为窗口缩放比例因子,范围在0~14,可以扩大当前窗口值为2^n。

  • 校验和

    长度为16位,防止传输过程中数据包有损坏,如果数据不正确,则等待重传。

  • 紧急指针

    长为16位,只有在URG控制位为1时才有效。

    表示本报文段中紧急数据的指针——数据首位到紧急数据指针之间的都是紧急数据。

    一般用于暂时中断通信

  • 可选项

    主要用于提高TCP的传输性能

类型 长度 意义 描述
2 4 Maximum Segment Size(MSS) TCP允许从对方接收的最大报文段
3 3 WSOPT-Window Scale 扩大窗口,可提高吞吐量
5 N SACK 选择确认选项
可以允许最大四次的方式确认应答,在数据时不时丢失的情况下,避免无用重发并提高重发速度。
8 10 TSOPT-Time Stamp Option 用于高速通信中对序列号的管理,可以区分新老序列号。

TSOPT-Time Stamp Option:记录序列号发送时的内核时间在报文中。

格式:kind(1字节)+length(1字节)+info(8字节)

按照上表来看,kind为8,length为10,info包含timestamptimestamp echo

可以解决以下两大问题:

  1. 计算往返时延(RTT)

    • 客户端向服务端发送数据时,timestamp存储当前内核时间ta1
    • 服务端回复客户端时,timestamp存放服务端内核时间tb
    • 客户端收到数据时,可以解析得到timestamp为客户端内核时间ta2,在解析timestamp echo得到第一次发送时的内核时间ta1,然后ta2-ta1就是RTT
  2. 防止序列号的回绕问题

    在传输不稳定的网络情况下,有可能会在较晚的时间内收到较早时发送的一个数据报,由于记录了timestamp那么可以比较timestamp来判断是否同一个数据报。

*TCP连接建立——三次握手

TCP建立连接-三次握手

过程

TCP三次握手过程

  • 初始时,客户端与服务端都处于CLOSED状态,服务端为了提供服务,主动监听端口,然后进入LISTEN状态
  • 客户端主动发起连接(发起SYN包),进入SYN-SENT状态。——第一次握手
  • 服务端收到SYN包后,回复SYN、ACK包,然后服务端进入SYN-RCVD状态——第二次握手
  • 客户端收到服务端发来的SYN、ACK包后,确认服务端通信建立,在回复ACK包,并进入ESTABLISHED状态;服务端接收到ACK之后,也变成了ESTABLISHED状态。——第三次握手

此时,双方进入了正常的数据传输过程。

  • SYN:sunchronize,该包需要对端的确认
  • ACK:acknowldgement

前两次握手过程中不能携带数据,第三次握手时可以携带数据。若有人在第一次发送的SYN包时注入大量数据,势必导致服务端要耗费更多的资源进行数据处理,降低了服务器的性能。

拓展

为什么不能两次握手?

根本原因:无法确认客户端的接收能力。

当客户端发送SYN后想进行第一次握手,但是由于网络原因导致该SYN包滞留而没有发送到服务端。此时触发超时重传机制,于是客户端会重新发出SYN包,由于是两次握手,客户端与服务端建好了连接。

但是过一段时间后,连接已经被关闭,这时上面被滞留的SYN包就可能会发送到服务端,此时服务端继续发送SYN、ACK包与客户端建立了连接。导致了连接资源的浪费

TCP攻击

利用TCP的三次握手机制,模拟多个客户端对服务端发起连接请求(发送SYN包),但是不处理服务端所返回的SYN、ACK包,导致服务端一直处于一种半连接状态,大量消耗服务器资源,导致死机。

即使配置了超时重试功能,也会因为数量过大,导致无限等待。

这个就叫做SYN FLOOD攻击,如何去应对上述攻击:

  • 增加SYN连接数,想办法增加半连接队列的容量
  • 减少SYN+ACK重试次数,避免堆积大量的超时重发任务
  • 也可以利用SYN Cookie技术,服务端收到SYN包后不先去分配资源,而是根据SYN包计算出一个SYN Cookie,在第二次握手时回复给客户端进行保存,这样在客户端回复ACK时带上SYN Cookie,服务端验证合法后就可以分配资源。
半连接/全连接队列

半连接队列:客户端发送SYN包到服务端后,服务端会回复ACK、SYN,并且切换状态至SYN-RECV,该连接就会进入半连接队列

全连接队列:客户端接收到服务端的ACK、SYN包后,三次握手即完成。这时,该连接就会加入至全连接队列

*TCP连接关闭——四次挥手

TCP断开连接——四次挥手

TCP四次挥手过程

过程

TCP四次挥手过程
  • 初始时,客户端与服务端都处于ESTABLISHED状态并双向传输数据
  • 客户端准备断开连接,然后发出FIN包并且指定一个seq序列号,发出后客户端进入FIN-WAIT-1状态——第一次挥手
  • 服务端收到FIN包后,回复ACK到客户端,并进入CLOSE-WAIT状态;客户端在收到服务端的ACK包后,也进入FIN-WAIT-2状态,等待服务端的最终释放连接报文(FIN)——第二次挥手
  • 服务端此时也要准备断开连接,发送FIN、ACK包到客户端,表示自己准备断开连接,然后进入LAST-ACK状态——第三次挥手
  • 客户端收到FIN、ACK消息后,会回复ACK包到服务端,并进入TIME-WAIT状态。出于稳定和安全性考虑,客户端会等待2MSL的时长,然后进入CLOSED状态。服务端在收到客户端的ACK包后,也进入CLOSED状态。——第四次挥手

此时,双方都进入CLOSED状态,表明正式断开了连接。

  • FIN:连接释放报文段

  • MSL:最长报文段寿命,表示任何报文段在网络上的最长存活时间,超过这个时间的报文都将被丢弃。

    协议规定MSL为2分钟,常用的多是30s、1min、2min

拓展

为什么要四次挥手?

当关闭连接时,客户端发送FIN报文到服务端,服务端先返回一个ACK报文到客户端,但是不会关闭掉当前的连接,这也导致会出现第三次挥手的情况,必须等到服务端任务处理完毕,才能发送FIN报文到客户端,通知客户端准备关闭。

如果服务端直接发送FIN、ACK包,如果此时网络出现延迟就会导致客户端触发超时重传一直发送FIN包,浪费连接资源。

为什么需要等待2MSL时间在关闭?
  • 首先,客户端如果不等待2MSL而是直接关闭,就会导致服务端后续发送的包无法被接收,并且客户端端口已被其他使用,就会产生无用数据,导致数据包混乱
  • 用一个MSL保证四次挥手中主动关闭方最后的ACK报文可以到达对方,上面示例表示的就是客户端发出的ACK包可以被服务端接收
  • 另一个MSL可以保证已失效的连接请求报文段不会出现在连接中,避免下一个新的连接出现旧的连接请求报文。

TCP状态机

将连接建立和断开的两个时序状态图结合起来,就是TCP状态机

TCP状态机

TCP快速打开——TCP Fast Open

TCP快速打开

TCP快速打开为了优化后续的TCP握手流程,快速建立连接。——TFO

主要利用的原理就是SYN Cookie

在第一次建立连接时,服务端会计算得出一个SYN Cookie,然后放置于SYN、ACK数据包中的FAST OPEN选项返回给客户端,客户端收到SYN、ACK数据包时,解析FAST_OPEN选项,缓存其中的Cookie。

在后续建立连接的过程中,客户端会直接发送SYN包、HTTP请求以及SYN Cookie到服务端,只要服务端验证SYN Cookie通过,就会返回SYN、ACK包以及HTTP请求的响应数据

TCP快速打开流程

*可靠性保证

无论对方以多快的的速度发送数据,接收方总来得及处理收到的数据

TCP可靠性保证

核心思想

  • 出错重传:出现错误时,让发送方重新发送数据
  • 速度匹配:当接收方来不及处理接收的数据时,通知发送方降低数据传输速率。

流量控制

接收方根据自己接收缓存的大小,动态调整发送窗口的大小,从而控制对方的发送速率。

滑动窗口协议

传输层进行流量控制的一种措施,接收方通过告知发送方自己的窗口大小,从而控制对方的发送速率,达到防止对方数据发送过快导致自己处理不完的问题。

滑动窗口分为以下两部分

  • 发送窗口

    任意时刻,发送方维持的一组连续的,允许发送帧的帧序号。

    滑动窗口协议-发送窗口

    其中黑框部分就是发送窗口。是由下面的已经发送但没有收到确认帧未发送但可以发送数据这两部分组成。


    发送窗口包含以下四大部分:

    • 已经发送并收到确认帧(Sent and Acknowledged)
    • 已经发送但没收到确认帧(Sent but not yet Acknowledged)
    • 未发送但可以发送(Not sent Recipient Ready to Receive)
    • 未发送且不能发送(Not send and not ready to receive)


    每收到一个接收方返回的确认帧,发送窗口就向前移动一帧。当发送窗口里都为已经发送但没收到确认帧,那么发送窗口停止发送数据,直到收到接收方发出的确认帧则继续向后移动,直到发送窗口内无数据可以发送。

  • 接收窗口

    任意时刻,接收方维持的一组连续的,允许接收帧的帧序号。

    滑动窗口协议-接收窗口

    其中黑框部分就是接收窗口。是由下面的未接受准备接收数据帧组成。

    接收窗口包含以下三大部分:

    • 已接收数据帧
    • 未接收但准备接收数据帧
    • 未接收且未准备接收数据帧


    每收到一个发送方发送的数据帧,接收窗口就向前移动一帧,并返回确认帧到发送端。

    若收到的数据帧不在接收窗口内,则丢弃该数据帧。

流量控制过程
  • 接收端向发送端通知自己可以接收的数据的大小a
  • 发送端就会发送不超过a大小的数据
  • 后续,接收端缓冲区面临溢出时,又会通知一个更小的窗口值b到发送端
  • 发送端收到通知后再发送不超过b大小的数据
流量控制过程
可能出现的问题

若接收端发给发送端的窗口调整通知中途丢失的话,可能会导致无法继续通信,甚至出现死锁问题(由于发送方一直等待接收方的窗口通知,然后接收方一直等到发送方的数据)。

解决方案:

TCP为每一个连接设计持续计时器,只要TCP发送方收到零窗口通知,就启动该计时器。若计时器结束,则会发出一个零窗口探测的数据报,等待对方给出窗口大小。为0则重新设置计时器,不为0则打破死锁状态。

死锁的形成条件:

  • 互斥条件:一个资源每次只能被一个进程使用
  • 占有且等待:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  • 不可强行占有:进程已获得的资源,未使用完之前不能强行剥夺
  • 循环等待:若干进程形成一种头尾相接的循环等待资源关系。

拥塞控制

防止过多的数据注入到网络中,使得网络中的路由器和链路不至于过载。

拥塞窗口(cwrd)

指目前自己还能传输的数据量大小

接收窗口拥塞窗口都是接收端上的概念,两者计算后可以得出发送窗口的大小。

1
发送窗口大小 = min(接收窗口大小,拥塞窗口大小)
慢启动

当主机开始发送数据时,由小到大逐渐增加拥塞窗口(发送窗口)数值,从而由小到大逐渐增大发送报文段。

得到的是慢启动阈值(ssthresh),当发送窗口到达这个阈值时,就先暂停一下发送。

拥塞避免

当发送窗口到达慢启动阈值时,使得拥塞窗口大小按规律线性增长。每经过一轮RTT(往返时延),拥塞窗口大小+1。

快速重传

TCP传输的过程中,如果发生了丢包——接收端发现数据端不是按序到达,那么就需要发送端重新发送丢失的数据。

例如发送了4 5 6 7四个数据报到接收端,但是接收端没有收到5,通常的情况下需要发送端等待一个RTO(超时重传时间)然后重新发送4 5 6 7数据报。

当发送方至少收到3个重复的ACK时,意识到丢包了,就会立即重新重传对方尚未收到的报文段,而不用等待一个RTO

选择性重传

当接收端已经收到了部分数据时,回复发送端ACK报文时,可以设置可选项(Option),加上SACK熟悉,通过left edgeright edge告知发送端已接收的数据范围。

然后发送端就会发送不在该范围内的数据给接收端,减少数据量。

快速恢复

当发送端接收到三次重复ACK之后,就会发现传输过程中出现了数据丢失,就会进入快速恢复阶段。

  • cwnd大小缩小为当前的一半
  • ssthresh设置为缩小后的cwnd大小
  • cwnd大小线性增加

提高网络利用率

TCP提高利用率

Nagle算法

发送端即使还有应该发送的数据,但如果这部分的数据还很少的话,则进行延迟发送。

主要为了避免小包的频繁发送。

立即发送条件

  • 已发送的数据都已经收到确认应答时
  • 数据包发送的大小达到MSS(最大报文长度)

可能的问题

可能产生某种程度的延迟。可以通过设置TCP_NODELAY关闭该算法。

延迟确认

接收端收到数据以后并不立即返回确认应答(ACK),而是等待一定时间内合并多个ACK在回复给发送端。

TCP要求这个时延必须小于500ms(可能导致发送端重新发送数据),一般操作系统不会设置超过200ms

立即回复条件

  • 需要调整发送窗口大小
  • TCP处于quickack模式,通过tcp_in_quickack_mode开启
  • 发现了乱序包

UDP协议

UDP协议

UDP协议位于传输层,但不保证可靠性。

特点

UDP特点 具体描述
无连接 使用UDP传输数据,不需要建立连接
不可靠 UDP的数据包发送后,不管其是否会到达接收方
面向报文 数据 以数据报文(包)的形式传输
无拥塞控制 由于是不可靠传输,不管是否到达接收方,所以不需要拥塞控制

UDP首部格式

UDP首部格式
  • 源端口号

    表示发送端端口号,长16位

    可以不设置该端口号,默认为0,表示单方面发送消息,不需要接受端的返回信息。

  • 目标端口号

    表示接收端端口号,长16位

  • UDP包长度

    保存了UDP首部的长度和数据的长度之和

  • UDP校验和

    为了提供可靠的UDP首部和数据而设计。检测用户数据报在传输过程中是否有错。

TCP、UDP的区别

  • TCP是面向连接的,UDP是面向无连接的

    所谓连接,是为了在客户端和服务端之间建立连接,而建立一定的数据结构来维护双方交互的状态,用这样的数据结构来保证所谓的面向连接的特性。

  • TCP提供可靠交付,通过TCP传输的数据,不丢失、无差错、不重复,按序到达。而UDP继承了IP包的特性,不保证不丢失,不保证按序到达。

  • TCP是面向字节流的,把每个数据块看为一串无结构的字节流,方便进行维护;而UDP继承IP包特性,基于数据报的,一个个的向外发或者接收。

  • TCP是有拥塞控制的,意识到丢包或者网络环境不好时,会调整自己的行为,调整发包频率;UDP只要有包就会发送,无论是否到达接收方。

  • TCP是有状态的服务,会记录当前状态,例如包是否发送,该发送哪个;UDP是无状态的,不会记录下来

适用场景

  1. 需要资源少,在网络情况比较好的内网,或者对于丢包不敏感的应用
  2. 不需要一对一沟通,建立连接,而是可以广播的应用
  3. 需要处理速度快,时延低,可以容忍少数丢包,但是要求即便网络堵塞,也不能受到影响。

例如:流媒体的协议(视频播放)实时游戏IoT物联网移动通信领域(语言、视频通话)

QUIC协议

Google提出的一种基于UDP改进的通信协议,目的是降低网络通信的延迟,提供更好的用户体验。该协议位于应用层上。

  • 自定义连接机制
  • 自定义重传机制
  • 无阻塞的多路复用
  • 自定义流量控制

IP协议

IPV4

Internet Protocol Version4

采用32位构成

示例:192.168.2.250

IPV6

Internet Protocol version 6

采用128位构成,按照每16位划分为一个段,将每个段转换为16进制,并用冒号隔开

示例:2404:6800:4012:200e

参考内容

图解TCP/IP

TCP快速打开

QUIC 协议原理分析


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!