TCP 的工作方式很清楚:它是有状态的,路由器可以代表端点维护状态。但不清楚 UDP 的工作方式。我想象一个不那么假设的场景:
路由器后面的 LAN 上的几个客户端运行即时通讯程序 X,该程序使用标准随机端口,例如 50000。它们将数据包发送到云中的服务器。它们的路由器知道:客户端 #1 从 IP 1.2.3.4 端口 5000 发送了一个 UDP 数据包;客户端 #2 从 IP 1.2.3.5 端口 50000 发送了一个 UDP 数据包;依此类推。现在服务器必须做出响应。
云中的服务器对此了解多少?在我看来,它知道 2.3.4.5 的路由器从端口 50000 向它发送了 2 个数据包。什么可以告诉它响应属于哪个 IM 客户端以及如何判断?在现实生活中如何解决这个问题?
答案1
严格来说,路由器不需要代表端点维护状态。它们需要做的就是转发 IP 数据包,而不管其内容如何。路由器只有在具有 a) 状态防火墙(用于允许单向连接)和/或 b) 状态 NAT(如家用路由器中常见的“多对一”伪装)时才需要保持每个连接的状态。但在这两种情况下,路由都是完全无状态的——它是防火墙在保持状态的同一路由器中共存。
但即使没有明确的信号(如来自 TCP SYN 或 FIN 数据包),防火墙和端点仍然可以保持状态 - 相反,防火墙会自动从每一个UDP 数据包通过它,并在超时后销毁它们。(同样,基于 UDP 的服务器将使用超时来自动清理状态。)
客户端向服务器发出的请求使用新的随机源端口。防火墙在状态表中没有该端口,因此会添加新的状态条目(通常超时时间为 30 秒),并且所有 NAT 映射都会照常记录在该状态条目中。
DNS 服务器的回复到达状态表中的目标端口,因此防火墙知道要将回复 NAT 到哪个内部主机。(通常,当防火墙看到双向通信时,它还会延长状态超时时间,例如延长到 120 秒。)
客户端和服务器继续在相同的端口之间交换 UDP 数据包,例如整个对话是在客户端:22162 ⇄ 服务器:53 之间进行的,因此数据包仍然与第一个数据包建立的防火墙状态相匹配,并且每个这样的数据包都会导致防火墙将状态到期超时更新为完整的 120 秒。
如果客户端和服务器之间超过 120 秒没有数据包,防火墙的状态将过期。因此,通过 UDP 实现长期通信的软件(例如游戏、IM 软件、VPN 隧道)通常会在空闲时发送“保持活动”数据包。
您始终可以构建有状态协议在上面无状态传输 – 就像 TCP 本身由无状态 IP 承载一样。唯一的区别是状态不是由操作系统管理(如 TCP),而是由服务器和客户端本身管理。
(一个很好的例子是 SCTP,它是一种像 TCP 一样的传输协议,通常由 OS 套接字处理,就像 TCP 一样,但也经常由程序本身使用 libsctp 实现并通过 UDP 传输。)