9.8. 端口

IP 地址说明一个数据包是发给哪个 主机 的。一台现代主机会同时运行许多程序——一个网页浏览器、一个聊天客户端、一个视频通话、一个备份任务——而它们每一个都在并行地收发数据包。IP 层无法把它们区分开来;它只是把每一个到达的数据包都交给“这台主机”。必须有某种机制来决定哪个数据包属于哪个程序。

端口号 就是答案。传输层的每个数据包在 IP 头部之外都携带两个额外字段:一个 源端口 和一个 目标端口。每个都是一个 16 位整数,因此每台主机有 65535 个可能的端口号。与 IP 地址相结合,端口便能标识出主机 内部 的一个特定端点——某个特定程序的某次特定会话。

右侧有一个单独的主机方框,顶部标注着一个 IP 地址。主机内部有三个程序,分别标注为“HTTP server”、 “video call”、“chat client”,每个都连接到 一个不同的端口号,分别标注为 80、5004 和 4321。三个箭头从网络到达 主机,每个都带有一个 目标端口标记;每个箭头都落在 相匹配的程序上。

许多程序共享同一个 IP 地址;目标端口把每个到达的数据包路由到正确的那个程序。

9.8.1. 知名端口

前 1024 个端口号按惯例保留给标准服务。读者会遇到的几个:

  • 22 —— SSH(Secure Shell 协议,用于加密的远程登录)。

  • 53 —— DNS,域名系统(在 名称与 DNS 中介绍)。

  • 80 —— HTTP,超文本传输协议——网络中未加密的协议。

  • 123 —— NTP,网络时间协议(设备如何从时间服务器设置自己的时钟)。

  • 443 —— HTTPS,承载于 TLS(传输层安全,互联网协议的标准加密封装)之上的 HTTP——它是浏览器中每一个显示锁形图标的网页背后的协议。

正是这套惯例,使得浏览器能够无需指定端口就连接到 http://example.com——它会假定使用 80,因为那是 HTTP 的知名端口。摄像头连接到一台网页服务器时也是同样的道理。

在 1024 以上,端口号不受限制,任何程序都可以占用其中之一。数据库服务器(PostgreSQL 用 5432,MySQL 用 3306)、应用服务器以及自定义协议,全都分布在更高的端口范围中的某处。

9.8.2. 临时端口

服务器在一个已知端口上监听。客户端在自己这一端使用一个 不同 的端口,每次发起出站连接时都会重新挑选。

当摄像头连接到一台网页服务器的 443 端口时,这次会话是发生在

camera IP : <some-port>     <-->     server IP : 443

<some-port> 是一个 临时端口——MicroPython 从一个较高的范围内挑选一个未被使用的号码,在连接期间使用它,之后再释放。脚本不必关心选中了哪个号码;套接字层会处理好这件事。

9.8.3. 监听与通信

一个端口所扮演的角色取决于它处于会话的哪一侧。有两种截然不同的情形:

  • 一个 监听 端口属于某个想要 接收 非请求连接的程序。该程序告诉 MicroPython“任何发往我 80 端口的传入数据包都是我的”,然后等待。服务器就是这样做的。

  • 一个 已连接 端口属于某个想要 发起 会话的程序。该程序挑选(或请求 MicroPython 挑选)一个临时端口,发送一个以该端口为源端口、以服务器的知名端口为目标端口的数据包,并在会话的其余部分使用同一对端口。

单个程序可以同时做这两件事,为每个角色各持有不同的端口。一个摄像头可能在 8000 端口上监听来自某个配置用户界面的入站 HTTP 连接,同时 在 443 端口上持有一条通往远程服务器的出站 HTTPS 连接。这两个角色互不干扰——每次会话都由完整的 (src IP, src port, dst IP, dst port) 四元组来标识,没有任何两次会话会共享相同的四元组。

9.8.4. 端口所开启的能力

有了端口之后,传输层终于能够解决 程序到程序 的投递问题。现在一个数据包携带了足够的信息,不仅能被路由到正确的 主机(IP 地址),还能被路由到该主机内部正确的 套接字(端口号)。

接下来的两页将介绍传输层在这套寻址机制之上提供的两种风格:UDP(用户数据报协议——每个数据包相互独立,没有任何保证)和 TCP(传输控制协议——一种面向连接、可靠且有序的字节流)。