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报文头固定有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的报文头只有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的三次握手四次挥手
三次握手、四次挥手
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是全双工通信)。
断开连接的过程:
- 主动关闭的一方(这里称之为客户端),发送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 数据包,将会消耗大量的计算机资源,导致正常的连接无法建立。