9.20. 總結¶
你已經走過了一則網路訊息從相機傳往外部世界沿途所穿越的各個層級:
動機——之所以需要網路,是因為只要超過幾台裝置需要通訊,或對方並不在同一條線路上,或多個程式同時共用同一條連結,點對點佈線就會在那一刻失去擴充能力。解答便是共用媒介、邏輯位址與路由。
分層模型——五個層級,每一層解決一個問題,並對下一層提供乾淨的介面。實體層與連結層處理直接相鄰節點之間的位元與影格,網路層處理橫跨網際網路的定址與路由,傳輸層處理程式對程式之間的傳遞,而應用層協定則建構在這一切之上。
底層各層——以太網路(Ethernet)與 Wi-Fi 作為實用的連結技術;MAC 位址在區域網段上識別硬體。相機的
network模組揭露了一個值得了解的旋鈕:要加入哪個 Wi-Fi 網路。在那之後,底下的一切都是自動的。網路層(IP)——IPv4 與 IPv6 位址識別主機,且不受主機接在哪條纜線上的影響。路由器在各個區域網段之間逐跳轉送封包,直到封包抵達。私有位址範圍與 NAT 正是為什麼家用與辦公室網路擁有各自的內部位址空間,而在邊界僅共用一個公開位址;對外的流量可自由運作,對內的流量則需要協助。
傳輸層——埠號識別主機內部的 程式;完整的
(IP, port)配對則識別出一個特定的 socket。UDP 是一層薄薄的封裝,每次送出一個自成一體、不提供任何保證的資料報——快速、低廉,且在可接受遺失時是正確的工具。TCP 是一種連線導向、可靠、有序的位元組串流——大部分網際網路流量的主力,其代價是一個往返的交握延遲。Python API——
socket.socket是這兩種協定共用的同一個類別。UDP 使用sendto()/recvfrom();TCP 則使用連線或監聽的模式,再加上send()/recv()。Socket 與asyncio搭配得乾淨俐落:asyncio.open_connection()與asyncio.start_server()會為每一條 TCP 連線提供一組 reader/writer 配對,使得眾多並行的通訊得以共用單一個事件迴圈,而無需用到執行緒。名稱與時間——
socket.getaddrinfo()會把像example.com這樣的名稱轉換成一個 IP 位址,可隨即交給 socket。network.hostname()設定相機自身的名稱,路由器會將其登錄於本地 DNS 之下,而相機內建的 mDNS 回應器也會以<name>.local的形式為其回應。ntptime.settime()則是把同樣的「在網路上查詢某件事物」的概念套用到實際的牆上時鐘時間,從公開的 NTP 伺服器取得 UTC 並設定板載時鐘。加密——
ssl將 socket 包裝在 TLS 之中。相機出貨時並未附帶任何憑證授權單位(certificate authority)儲存區,因此開箱即用時你只會得到加密——通訊內容不再是明文,但相機並不會驗證回應者究竟是誰。若要進行真正的身分驗證——驗證公開的 HTTPS 伺服器、將相機作為 TLS 伺服器執行、雙向 TLS——以憑證為基礎的工作流程涵蓋於 使用 TLS 憑證。DTLS(執行於 UDP 之上的 TLS)以相同的方式使用相同的模組。一個真實的應用層協定——以 MQTT 作為它底下每一層串接在一起的實例。一個 1 位元組的型別與旗標位元組、一個可變長度的剩餘長度欄位、一個以長度為前綴的 UTF-8 主題(topic),以及酬載,全都透過 TCP(並可選擇性地置於 TLS 之內)傳送至一個 broker,再由它把訊息扇出(fan out)給該主題上的每一位訂閱者。隨附的
mqtt用戶端把這套線路格式包裝成一組connect/publish/subscribeAPI,小到可以一口氣讀完。
這已足以撰寫能與其他機器通訊、向遠端服務發布資料、接受來自區域網路上用戶端的連線,並能在處理相機其餘工作的同時並行完成上述一切的相機應用程式。
9.20.1. 日後運用本參考資料¶
請把這些網路章節當作參考資料;日後回來查閱 UDP 監聽器的確切形態,或 TLS 搭配 asyncio 的模式,正是它預期的用途。當問題只是「這個呼叫的確切名稱是什麼」時,network --- 網路設定、socket --- socket 模組、ssl --- SSL/TLS 模組 與 ntptime --- 簡易 NTP 用戶端 參考頁面會把每一個方法、旗標與常數都集中列在同一處。
9.20.2. 接下來該往哪裡走¶
網頁伺服器 是下一個重要主題。在 socket 已能運作、TLS 也已可用的情況下,自然往上的下一層就是建構在它們之上的各種協定:用於提供內容與 API 的 HTTP、用於讓連線雙向保持開啟的 WebSocket,以及那些隱藏樣板程式碼的小型框架。本節中的一切都會延續下去——畢竟,網頁伺服器說到底,不過就是一台在它所接受的 socket 上講 HTTP 的 TCP 伺服器,而且往往會用 TLS 把整件事包裝起來。