9.8. 端口¶
IP 地址说明一个数据包是发给哪个 主机 的。一台现代主机会同时运行许多程序——一个网页浏览器、一个聊天客户端、一个视频通话、一个备份任务——而它们每一个都在并行地收发数据包。IP 层无法把它们区分开来;它只是把每一个到达的数据包都交给“这台主机”。必须有某种机制来决定哪个数据包属于哪个程序。
端口号 就是答案。传输层的每个数据包在 IP 头部之外都携带两个额外字段:一个 源端口 和一个 目标端口。每个都是一个 16 位整数,因此每台主机有 65535 个可能的端口号。与 IP 地址相结合,端口便能标识出主机 内部 的一个特定端点——某个特定程序的某次特定会话。
许多程序共享同一个 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(传输控制协议——一种面向连接、可靠且有序的字节流)。