ARP协议
ARP (Address Resolution Protocol),即“地址解析协议”, 用于在一个局域网(LAN)内,将一个已知的IP地址(逻辑地址)解析(翻译)成对应的MAC地址(物理地址)。
当主机A想要给同一局域网内的主机B发送数据时,主机A通常只知道主机B的IP地址(例如,你在ping 192.168.1.101)。但是,数据在局域网内最终是以数据帧的形式传播的,而帧的头部必须包含目标MAC地址。交换机根据这个MAC地址来转发帧。因此,主机A在发送数据之前,必须先获得主机B的MAC地址。
ARP的工作原理
ARP的工作过程可以分为两个核心步骤:ARP请求 和 ARP应答。我们通过一个例子来说明。
假设局域网内有两台主机:
- 主机A: IP地址 192.168.1.100,MAC地址 AA-AA-AA-AA-AA-AA
- 主机B: IP地址 192.168.1.101,MAC地址 BB-BB-BB-BB-BB-BB
现在,主机A 想要向 主机B 发送数据。
步骤1:检查ARP缓存 主机A首先会检查自己的 ARP缓存表(ARP Cache)。这是一个存储了近期IP地址与MAC地址映射关系的动态表格(为了提高效率,避免每次通信都进行一次广播查询)。
如果找到 192.168.1.101 对应的MAC地址,则直接使用该地址封装数据帧并发送,整个ARP过程结束; 如果没有找到,则进入下一步。
步骤2:发送ARP请求(广播) 主机A会在局域网内发送一个 ARP请求(ARP Request) 报文。
这个请求的核心信息是:“谁的IP地址是 192.168.1.101?请告诉我的IP地址 192.168.1.100,我的MAC地址是 AA-AA-AA-AA-AA-AA”。
这个ARP请求报文会被封装在一个以太网帧中。这个帧的特殊之处在于源MAC地址是主机A的MAC地址 (AA-AA-AA-AA-AA-AA), 目标MAC地址却是一个特殊的广播地址 (FF-FF-FF-FF-FF-FF)。使用广播地址意味着这个帧会被发送到该局域网内的所有设备。
步骤3:网络中所有主机处理ARP请求 局域网内的所有设备(包括主机B和其他主机)都会接收到这个广播帧。
其他主机会检查ARP请求中的目标IP地址(192.168.1.101),发现与自身的IP地址不符,因此会静默丢弃这个请求,不作任何响应。
而主机发现ARP请求中的目标IP地址正是自己的IP地址, 执行下一个阶段。
步骤4:发送ARP应答(单播) 主机B在确认请求是发给自己的之后,会构造一个 ARP应答(ARP Reply) 报文。
报文内容:应答的核心信息是:“我就是 192.168.1.101,我的MAC地址是 BB-BB-BB-BB-BB-BB”。
这个ARP应答报文也会被封装在一个以太网帧中,但这次是单播:源MAC地址是主机B的MAC地址 (BB-BB-BB-BB-BB-BB), 目标MAC地址是主机A的MAC地址 (AA-AA-AA-AA-AA-AA)。主机B之所以知道主机A的MAC地址,是因为它在最初的ARP请求中已经包含了。
单播可以直接将应答信息发送给请求者(主机A),而不需要再打扰网络中的其他设备。
步骤5:更新ARP缓存并通信 主机A收到主机B的ARP应答后,就获取了 192.168.1.101 对应的MAC地址 BB-BB-BB-BB-BB-BB。
接着, 主机A会将这个映射关系存入自己的ARP缓存表,并设置一个老化时间(例如2分钟)。在老化时间内,再次向 192.168.1.101 发送数据时,就可以直接从缓存中查找,无需再次发送ARP请求。
主机A现在可以愉快地将数据封装成以太网帧(目标MAC为BB-BB-BB-BB-BB-BB),并通过交换机准确地发送给主机B了。
总之, ARP 协议可以认为是网络层在与数据链路层交互时所使用的一个“工具”或“接口”, 利用网络层的IP地址获取数据链路层的MAC地址,以便在物理网络上进行通信
ARP 数据报格式
IP协议
IP地址: 可以在网络环境中, 唯一标识一台主机 端口号: 可以在网络的一台主机中, 唯一标识一个进程 IP地址+端口号: 可以在网络环境中, 唯一标识一台主机的一个进程
TCP三次握手与四次挥手
TCP 协议中至关重要的两个过程:三次握手(用于建立连接)和四次挥手(用于断开连接)是确保 TCP 协议实现可靠、面向连接的通信基础。
TCP 报文段
在开始之前,我们先了解几个 TCP 头部中起关键作用的标志位 (Flags):
- SYN (Synchronize): 请求建立连接。在三次握手中的前两次会使用。
- ACK (Acknowledgement): 确认号字段有效。表示对收到数据的确认。
- FIN (Finish): 请求断开连接。表示发送方已经没有数据要发送了。
- seq (Sequence Number): 序列号。用于标识 TCP 数据段中数据的字节流顺序。
- ack (Acknowledgement Number): 确认号。表示期望收到对方下一个数据段的起始序列号。
三次握手 (Three-Way Handshake)
TCP 是一个面向连接的协议,在数据传输开始前,客户端和服务器必须先建立一个可靠的连接。这个过程就是“三次握手”。
核心目的是确认双方的收发能力, 确保客户端能发能收,服务器也能发能收; 还可以同步初始序列号 (ISN), 双方协商一个初始序列号,为后续可靠的数据传输做准备。
第一次握手 (Client → Server)
- 动作:客户端向服务器发送一个连接请求报文段。
- 标志位:SYN = 1, ACK = 0。
- 序列号:客户端随机选择一个初始序列号 seq = x。
- 状态:客户端进入 SYN_SENT 状态。
- 含义:“你好,服务器。我想和你建立连接,我的初始序列号是 x。”
第二次握手 (Server → Client)
- 动作:服务器收到客户端的请求后,回复一个确认报文段。
- 标志位:SYN = 1, ACK = 1。
- 序列号:服务器也随机选择一个自己的初始序列号 seq = y。
- 确认号:ack = x + 1。这个值表示“我已成功收到你的序列号 x,希望下一个收到的数据从 x+1 开始”。
- 状态:服务器进入 SYN_RCVD 状态。
- 含义:“好的,我收到了你的请求。我也准备好了,我的初始序列号是 y。同时,我确认收到了你的 x。”
第三次握手 (Client → Server)
- 动作:客户端收到服务器的确认后,再发送一个确认报文段。
- 标志位:SYN = 0, ACK = 1。
- 序列号:seq = x + 1。
- 确认号:ack = y + 1。表示“我已成功收到你的序列号 y,希望下一个收到的数据从 y+1 开始”。
- 状态:客户端进入 ESTABLISHED 状态。服务器收到这个确认后,也进入 ESTABLISHED 状态。
- 含义:“好的,我收到了你的确认。现在连接正式建立,我们可以开始传输数据了。”
为什么需要三次握手,而不是两次?这是为了防止已失效的连接请求报文段(第一次)突然又传送到了服务器,从而产生错误。如果是两次握手,服务器收到这个失效的请求后会立即建立连接,并等待客户端发送数据,这会浪费服务器资源。而三次握手时,客户端不会对这个过时的确认进行响应,服务器也就不会建立错误的连接。
四次挥手 (Four-Way Handshake)
当数据传输结束后,通信双方都可以发起请求来终止连接。由于 TCP 连接是全双工的(数据可以在两个方向上同时传输),因此断开连接需要双方各自关闭自己的发送通道。这个过程就是“四次挥手”。
核心目的是安全、优雅地关闭连接,确保双方所有待发送的数据都已发送完毕。
第一次挥手 (Client → Server)
- 动作:客户端(主动关闭方)发送一个连接释放报文段。
- 标志位:FIN = 1。
- 序列号:seq = u (u 是客户端已发送数据的最后一个字节的序列号+1)。
- 状态:客户端进入 FIN_WAIT_1 状态。
- 含义:“我这边的数据已经全部发送完了,我要关闭发送通道了。”
第二次挥手 (Server → Client)
- 动作:服务器(被动关闭方)收到 FIN 后,发送一个确认报文段。
- 标志位:ACK = 1。
- 确认号:ack = u + 1。
- 状态:服务器进入 CLOSE_WAIT 状态。客户端收到后,进入 FIN_WAIT_2 状态。
- 含义:“收到了你的关闭请求。但请等一下,我可能还有数据没有发完。”
- 注意:此时连接处于半关闭 (Half-Close) 状态。客户端不能再发送数据,但服务器仍然可以向客户端发送数据, 客户端可以接受。
第三次挥手 (Server → Client)
- 动作:服务器确认自己所有的数据也发送完毕后,向客户端发送一个连接释放报文段。
- 标志位:FIN = 1, ACK = 1。
- 序列号:seq = v (v 是服务器已发送数据的最后一个字节的序列号+1)。
- 确认号:ack = u + 1。
- 状态:服务器进入 LAST_ACK 状态,等待客户端的最后确认。
- 含义:“我这边的数据也全部发送完了,现在我也要关闭发送通道了。”
第四次挥手 (Client → Server)
- 动作:客户端收到服务器的 FIN 后,发送最后一个确认报文段。
- 标志位:ACK = 1。
- 确认号:ack = v + 1。
- 状态:客户端进入 TIME_WAIT 状态。经过 2MSL(两倍报文最大生存时间)后,客户端才进入 CLOSED 状态。服务器收到这个 ACK 后,立即进入 CLOSED 状态。
- 含义:“好的,我也收到了你的关闭请求。连接正式关闭。”
为什么挥手需要四次?因为 TCP 是全双工的。当一方(客户端)发送 FIN 请求关闭时,只表示它不会再发送数据了,但仍然可以接收数据。另一方(服务器)收到后,会先回复一个 ACK 确认,但它可能还有数据需要处理和发送,所以不能立即发送自己的 FIN。只有当服务器也准备好关闭时,才会发送自己的 FIN。因此,ACK 和 FIN 通常是分开发送的,这就构成了四次挥手。
为什么客户端最后要进入 TIME_WAIT 状态并等待 2MSL?确保最后的 ACK 能到达服务器:如果这个 ACK 丢失,服务器会超时重传 FIN。TIME_WAIT 状态可以确保客户端有足够的时间来响应这个重传,从而使服务器能够正常关闭; 另一方面,等待 2MSL 可以确保本次连接中产生的所有报文段都从网络中消失,从而避免影响到下一个使用相同端口号的新连接。
TCP连接状态图
这张图精准地描述了一个 TCP 连接从创建到销毁的整个生命周期中所有可能的状态,以及在不同事件(如收到一个数据包、应用程序发起一个操作)驱动下,状态之间是如何转换的。
- 方框:代表 TCP 连接可能处于的状态 (State),例如 LISTEN, ESTABLISHED。
- 箭头:代表状态的转换 (Transition)。
- 箭头上的文字:代表触发这个转换的事件 (Event) 或 动作 (Action)。
整个过程可以分为三个主要部分:
- 连接建立(三次握手):图的上半部分,从 CLOSED 到 ESTABLISHED。
- 数据传输:中间的核心状态 ESTABLISHED。
- 连接断开(四次挥手):图的下半部分,从 ESTABLISHED 回到 CLOSED。
第一阶段:连接建立(三次握手)
这个过程涉及两方:客户端(主动打开方) 和 服务器(被动打开方)。
- 服务器的路径(被动打开)
- CLOSED → LISTEN:初始状态是 CLOSED(关闭), 服务器调用 listen() 函数后,进入 LISTEN 状态,开始监听指定的端口,等待客户端的连接请求。
- LISTEN → SYN_RCVD:
- 事件:在 LISTEN 状态下,服务器收到了一个来自客户端的 SYN 请求报文。
- 动作:服务器发送自己的 SYN 和对客户端 SYN 的 ACK 作为响应(即第二次握手)。
- 转换:服务器状态变为 SYN_RCVD (SYN Received),表示已收到连接请求,正在等待客户端的最终确认。
- SYN_RCVD → ESTABLISHED:
- 事件:在 SYN_RCVD 状态下,服务器收到了客户端发来的 ACK 确认报文(即第三次握手)。
- 转换:服务器状态变为 ESTABLISHED,表示连接已成功建立,可以开始数据传输。
- 客户端的路径(主动打开)
- CLOSED → SYN_SENT:初始状态是 CLOSED, 客户端调用 connect() 函数,主动打开连接,并发送一个 SYN 请求报文(即第一次握手), 此时客户端状态变为 SYN_SENT (SYN Sent),表示已发送连接请求,正在等待服务器的响应。
- SYN_SENT → ESTABLISHED:
- 事件:在 SYN_SENT 状态下,客户端收到了服务器的 SYN 和 ACK 报文(即第二次握手)。
- 动作:客户端发送最后一个 ACK 确认报文(即第三次握手)。
- 转换:客户端状态变为 ESTABLISHED,连接成功建立。
第二阶段:数据传输
ESTABLISHED:这是整个 TCP 连接的核心状态。当客户端和服务器都处于此状态时,它们可以自由地、双向地进行数据传输。图中的“数据传送阶段”指的就是这个状态。
第三阶段:连接断开(四次挥手)
这个过程同样涉及两方:主动关闭方和被动关闭方。我们以客户端主动关闭为例。
- 客户端的路径(主动关闭方)
- ESTABLISHED → FIN_WAIT_1:
- 动作:客户端应用程序调用 close(),主动关闭连接,发送一个 FIN 报文,表示自己没有数据要发送了。
- 转换:客户端进入 FIN_WAIT_1 状态。
- FIN_WAIT_1 → FIN_WAIT_2:
- 事件:客户端收到了服务器对自己 FIN 报文的 ACK 确认。
- 转换:客户端进入 FIN_WAIT_2 状态。此时连接处于半关闭状态,客户端不能再发送数据,但可以接收来自服务器的数据。
- FIN_WAIT_2 → TIME_WAIT:
- 事件:客户端收到了服务器发送的 FIN 报文,表示服务器也准备关闭连接了。
- 动作:客户端发送最后一个 ACK 作为确认。
- 转换:客户端进入 TIME_WAIT 状态。
- TIME_WAIT → CLOSED:
- 事件:TIME_WAIT 是一个等待状态,通常会持续 2MSL(两倍报文最大生存时间)。
- 目的:确保网络中所有与此连接相关的报文都已消失,并能处理可能丢失的最后一个 ACK。
- 转换:等待计时结束后,连接彻底关闭,状态回到 CLOSED。
- ESTABLISHED → FIN_WAIT_1:
- 服务器的路径(被动关闭方)
- ESTABLISHED → CLOSE_WAIT:
- 事件:服务器收到了来自客户端的 FIN 报文。
- 动作:服务器发送一个 ACK 进行确认。
- 转换:服务器进入 CLOSE_WAIT 状态。这个状态告诉上层应用程序:“对方已经关闭了连接,请处理剩余的数据并准备关闭”。
- CLOSE_WAIT → LAST_ACK:
- 动作:服务器的应用程序完成数据处理后,调用 close(),发送自己的 FIN 报文。
- 转换:服务器进入 LAST_ACK 状态,等待客户端的最后确认。
- LAST_ACK → CLOSED:
- 事件:服务器收到了客户端发来的最后一个 ACK。
- 转换:服务器状态变为 CLOSED,连接彻底关闭。
- ESTABLISHED → CLOSE_WAIT:
图中还包含了一些特殊的状态转换,例如:
- 同时打开 (SYN_SENT → SYN_RCVD):双方同时向对方发送 SYN 请求,这种情况比较罕见。
- 同时关闭 (FIN_WAIT_1 → CLOSING):双方同时向对方发送 FIN 请求,会进入 CLOSING 状态,等待收到对方的 ACK 后再进入 TIME_WAIT。