TCP/UDP相关基础知识小结

Posted by RussXia on April 3, 2020

OSI七层模型、TCP/IP四层协议、五层协议

对于通信系统的分层,通常有三种分层模型:

  • OSI七层模型 (7层):物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。

  • TCP/IP四层模型(4层):网络接口层、 网际层、运输层、 应用层。

  • 五层模型 (5层):物理层、数据链路层、网络层、运输层、 应用层。

    分层模型

OSI的七层模型,是一个理论性的网络标准化协议。但是实践过程中,由于OSI七层过于严格,因而基于其衍生出了TCP/IP四层模型。

至于五层协议模型,是OSI和TCP/IP结合的非官方的协议模型,主要是将TCP/IP协议模型中的网络接口层进一步划分为数据链路层物理层

网络协议在分层模型中的位置

TCP和UDP

TCP和UDP是穿出层的两个最著名的传输协议。

  • TCP协议是面向连接的可靠的面向字节流的传输层通信协议。
  • UDP协议是无连接的不可靠的面向报文的传输层通信协议。可以单播、多播、广播。
  TCP UDP
是否连接 面向连接 单元格
是否可靠 可靠传输(确认机制、流量控制、拥塞控制) 不可靠,不会重传
传输方式 面向字节流 面向报文
通信方式 一对一通信 支持一对一、一对多、多对一和多对多通信
适用场景 要求可靠传输,如文件传输等 不要求可靠传输,如视频、语音、直播等。

TCP和UDP的报文结构

TCP报文结构

TCP报文头固定有20个字节,选项部分长度可变。在有选项的情况下,TCP首部最大可能有 60 字节。

  • 源端口、目的端口:和IP协议中的源IP、目的IP一起确定每一个确定的连接。(source IP:port –> destination IP:port)
  • 序号 (Sequence Number):TCP会隐式地对字节流中每个字节编号, TCP 报文段中的序号用于标识当前报文第一个字节的序列号。
  • 确认号 (Acknowledgment Number):期望收到的下一个序列号。只有 ACK 标志为 1 时确认序号字段才有效,为上次收到的数据字节序号+1。
  • 首部长度 (数据偏移,Header Length):由于选项部分长度可变,所以需要首部长度字段明确表示首部的长度。
  • 保留字段 (Reserved):保留,尚未使用
  • 控制标记位,一共6bit,表示6个控制标记:
    • URG (Urgent Bit):紧急指针是否有效
    • ACK (Acknowledgment Bit):确认序号是否有效
    • PSH (Push Bit):接收方是否应尽快将这个报文段交给应用层
    • RST (Reset Bit):重建连接
    • SYN (Synchronize Bit):用于发起一个连接
    • FIN (Finish Bit):用于关闭连接
  • 窗口 (Window):用于指定流控窗口的大小,一共16bit,单位是字节,所以窗口大小最大为65535字节。
  • 校验和 (Checksum):用于校验数据的完整性。
  • 紧急指针 (Urgent Pointer):当URG为1时生效,用于TCP发送端向接受端发送紧急数据。
  • 选项 (Options):可选字段,最常见的用法是指定 “最长报文大小 (MSS,Maximum Segment Size)”。

UDP报文结构

UDP的报文头只有8个字节,所以头部开销很小,传输数据报文更高效。

  • 源端口、目的端口:和TCP中的源端口、目的端口类似。
  • 长度:UDP头部+报文数据的总长度
  • 校验和:和TCP中的校验和类似,用于校验数据的完整性。

如何理解TCP是面向字节流,UDP是面向报文的

面向报文和面向字节流的区别,主要在于读取数据时,是否是有”边界”的。

所以没有边界的,就是字节流(流,持续不断);有边界的,就是报文段。

对于整个应用程序来说

  • UDP你发一个报文,则接受方就收到一个报文(可能会丢失,也可能会乱序),UDP既不会拆分,也不会合并,应用程序可以将一个报文看成一个整体。
  • TCP的数据时以字节为单位的,比如1Kb的数据会被拆分成几次才能发送完,这多次的TCP数据包,可以看成一个整体,它们是连贯的、有序的、完整的。

所以,有时候我们也会说,TCP是无界的,UDP是有界的

TCP和UDP的主要区别总结

  • TCP是面向连接的,UDP是无连接的
  • TCP无界(面向字节流),UDP是有界的(面向报文)
  • TCP可靠,UDP不可靠
  • TCP有序,UDP无序
  • TCP有流量控制(拥塞控制),UDP没有
  • TCP的报文头比较大(固定20个字节,最大可达60字节),UDP的比较小(8个字节)

TCP的三次握手四次挥手

三次握手、四次挥手

三次握手过程.png

TCP三次握手建立连接,四次挥手关闭连接。

建立连接过程:

  • 第一次握手,客户端向服务端发送 SYN=1,seq_num(序号)=x序列号的报文(SYN标记位用于发起一个连接)
  • 服务端接受到客户端的报文,返回SYN=1,seq_num=y;ACK=1,ack_num(确认序号)=x+1的报文
  • 客户端返回 SYN=0,seq_num=x+1;ACK=1,ack_num=y+1

这一过程被称为三次握手,完成三次握手后,双方可以互相发送数据(TCP是全双工通信)。

四次挥手过程.png

断开连接的过程:

  • 主动关闭的一方(这里称之为客户端),发送FIN=1,seq_num=x报文,表明自己已经没有要发送的报文
  • 服务端接受到报文,返回ACK=1,ack=x+1,seq=y报文,此时服务端进入CLOSE-WAIT(半关闭)的状态。服务端依然可以继续发送数据,客户端也依然能够接收。
  • 服务端发送完数据包后,会向客户端发送 FIN =1,ACK=1,ack_num=p+1,seq_num=q
  • 客户端回复ACK=1,seq_num=t,ack_num=q+1

因为TCP是全双工通信,所以这个断开连接的发起可能是正在通信双方中的任意一方。

客户端在最后一次回复ACK的报文后,会进入TIME-WAIT状态,等待2 * MSL(2 *Maximum Segment Lifetime,两倍的报文段最大存活时间),如无特殊情况,才会关闭连接。服务端在接受到客户端最后的ACK报文后,进入CLOSED状态。

这样设计的原因是,服务端在发送FIN时,如果没有接收到客户端回复的最后的那个ACK报文,会重复发送FIN请求。而客户端等待2 * MSL,就是为了防止出现服务端一直没有FIN的ACK,一直停留在 LAST-ACK状态。

TCP需要三次握手,才能确定双方的接受和发送能力正常。而一端关闭连接时,另一端可能还有一些数据要传输,并不能立即关闭连接,所以需要四次挥手才能断开连接。

SYN Flood攻击

SYN Flood Attack,又叫半开放攻击、SYN洪水攻击等,是一种拒绝服务(DDoS)攻击。那么SYN Flood是如何攻击的呢。

我们知道,TCP三次握手建立连接。当服务端收到SYN后,服务端进入SYN_RECEIVED状态,同时服务端会维护一个半连接队列,用来维护那些发起但是未完成握手的连接。此时,如果攻击者在短时间内发送大量的SYN包而不响应,这个半连接队列就会被撑满,而且服务端不确定SYN+ACK是否发送成功,会进行n次重试(tcp_syn_retries,默认5次),每次SYN-ACK发送的间隔翻倍(e.g. 1s,2s,4s,8s,16s,32s,一次正常,五次重试)。

一般来说,SYN Flood攻击,会伪造来源IP(ip报文中),重复发送SYN请求到服务端。对于服务端而言,IP地址是伪造的,所以服务端的SYN+ACK回应,是肯定收不到客户端的ACK回复的。

针对SYN Flood攻击,可以采取的几种常见防御措施:

  • 增加队列长度(tcp_max_syn_backlog):当等待的请求数大于 tcp_max_syn_backlog 时,后面的请求就会被丢弃,适当增加 tcp_max_syn_backlog 可以提高握手成功率,推荐大于1024。
  • 减少重试次数(tcp_synack_retries):对于半连接队列中,超时未收到SYN+ACK的回复的连接,服务端会进行重试,适当减少重试次数,可以更快释放那些伪造的连接。
  • SYN Cookie 技术:当连接超过了 tcp_max_syn_backlog 时,如果内核启用了 SYN Cookie ,就不再把请求放到半连接队列中,而是用 SYN Cookie 来校验。

ACK Flood攻击

ACK Flood Attack也是利用TCP三次握手的漏洞来实现攻击的。ACK Flood 攻击利用的是三次握手中的第二次握手。攻击者伪造大量的虚假的ACK数据包给目标主机,目标主机收到ACK数据包,会去自己的TCP表中查询是否存在这个发起的SYN连接,如果有则发送第三次握手请求,成功建立起连接;如果没有,则发送 ACK + RST 断开连接。

这个校验是否存在于自己的TCP表中,然后发送第三次握手请求建立连接,或者发送 ACK + RST 断开连接势必都会消耗一定的CPU资源。如果瞬间接受到海量的 SYN + ACK 数据包,将会消耗大量的计算机资源,导致正常的连接无法建立。