從 ALOHA 到先聽再說
在前一篇導覽裡,我們認識了多重存取問題:許多站台共用一條通道——一條纜線、一段無線電頻率——若兩者在同一瞬間發送,它們的訊號就會重疊、把彼此攪成一團,造成一次碰撞。我們看過三大類答案,而其中最有意思的是隨機存取,它的祖先就是 ALOHA:有資料就直接發送,若訊框毀損了,就等一小段隨機的時間再試一次。ALOHA 簡單得令人讚嘆,卻也很沒禮貌——它說話前不檢查是否已有人在說話——所以在高負載下,通道大半都浪費在碰撞上了。
解法是一條你在對話中早已實踐的小小禮貌:開口之前,先停下來聽。一個站台若先檢查通道是否忙碌、只在聽起來空閒時才發送,它跑的就是 CSMA——載波感測多重存取。所謂「載波感測」,不過就是「聽聽線上有沒有別人的訊號」。這跟你不會貿然闖進兩個人正講到一半的房間,是同一種直覺。光是這一條規則,相較於樸素的 ALOHA,就大幅減少了碰撞,因為大多數時候你都能聽出通道已被佔用,於是有禮貌地先按兵不動。
當碰撞仍然發生:CSMA/CD
在古老的有線乙太網路上,當大家共用一條電氣匯流排時,工程師替 CSMA 加上了第二條禮貌:說話的同時,繼續聽。如果你聽見自己發出的訊號被別人攪亂了,你就知道此刻正在發生碰撞。這就是 CSMA/CD——帶碰撞偵測的載波感測多重存取。它的好處是:發送方不必把一整個注定失敗的訊框送完、事後才得知它丟了。它在幾微秒內就偵測到這場車禍、猛踩剎車並復原——把浪費一整個訊框,變成只浪費其中一小片段。
- 感測通道。若它一直空閒,就開始發送你的訊框;若它忙碌,就等到它空閒(接著可選擇再多等一個極小的節拍)。
- 發送的同時,持續聆聽。把線上的訊號和你正在送的相比對——若兩者不同,就表示另有他人也在說話:一次碰撞。
- 一旦偵測到碰撞,立刻停止發送該訊框,並廣播一段短短的「干擾」訊號,好讓每個站台都確定碰撞發生了。
- 退避一段隨機長度的時間,然後回到步驟 1 再試一次——這份隨機性,能讓相撞的各站台不至於又步調一致地再撞一次。
巧妙之處在於退避如何隨之擴張。乙太網路採用「二元指數退避」:第一次碰撞後,站台從 {0, 1} 個時槽時間中挑選等待長度;第二次後,從 {0, 1, 2, 3} 中挑;第 n 次碰撞後,則從上至 2^n - 1 的範圍中挑。愈擁擠,各站台就把重試攤得愈開,於是通道會自行平靜下來,而不會熔成一場重試的風暴。這跟你稍後會在 TCP 裡遇到的,是同一種溫和的智慧——一位謹慎的駕駛會在成功後溫和地加速、在出狀況後猛力剎車。
為什麼 Wi-Fi 偵測不到碰撞——所以它改為避免
這裡有一個真正令人意外、幾乎把每個人都絆倒的事實:Wi-Fi 並不使用 CSMA/CD。它用的是 CSMA/CA,CA 代表「碰撞避免」。原因是物理性且無可迴避的:一具無線電,沒辦法在自家發射機正貼著自家天線全功率轟鳴時,去聽一個微弱的碰撞——它自己的訊號會把其他一切都淹沒。一條線可以比較進來的電壓和送出去的電壓,但一具無線電在它一開口的瞬間,實際上就聾了。所以 Wi-Fi 無法在半空中偵測碰撞;它必須從一開始就費力地去避免碰撞。
避免靠的是額外的謹慎。Wi-Fi 站台會感測通道,而即使通道轉為空閒,它在發送前也會先等一個固定間隔再加上一段隨機退避,這樣兩個等待中的站台就不太可能在同一瞬間躍出。關鍵在於:因為發送方聽不到碰撞,它便要求接收方確認成功——每收到一個訊框,都會回以一個明確的確認。沒有回傳確認,就代表「假設它撞了或丟了——退避並重傳」。這正是連結層在跳它自己那支小小的可靠傳遞之舞,也就是你在本層稍早遇過的 ARQ 觀念,落實在第二層上。
MAC 位址:每個訊框上的一個扁平名字
前述這一切的輪流與退避,決定的是「誰能開口說話」。但你一旦開口,正確的那位鄰居怎麼知道這個訊框是給他的、而其他所有人又怎麼知道該忽略它?這正是 MAC 位址的工作——一個 48 位元的識別碼,出廠時就燒進每張網路介面卡,通常寫成六組十六進位數字對,例如 00:1B:44:11:3A:B7。這裡的「MAC」就是 媒體存取控制,也就是跑 CSMA 的同一個子層。區域網路上的每個訊框,標頭裡都帶著一個目的 MAC 位址與一個來源 MAC 位址;介面會撈起定址給它自己 MAC(或給全為 1 的廣播位址)的訊框,讓其餘的從旁滑過。
Ethernet frame (the link-layer envelope) +-----------------+-----------------+--------+---------------+-----+ | dest MAC (6 B) | src MAC (6 B) | type | payload (IP) | FCS | +-----------------+-----------------+--------+---------------+-----+ 00:1B:44:11:3A:B7 AA:BB:CC:00:11:22 0x0800 ...your packet... CRC FF:FF:FF:FF:FF:FF = broadcast: every interface on the LAN reads it
有兩件事,讓 MAC 位址感覺上與你或許聽過的 IP 位址不同。第一,它是扁平的、而非階層式的:00:1B:44:11:3A:B7 完全不告訴你裝置在哪裡,不像街道地址會告訴你城市與街道。前半段標明製造商,後半段只是個序號,所以 MAC 命名的是一件硬體、而非一個位置——這正是為什麼它只在跨越單一一條本地連結的那一跳裡管用,且光靠自己無法在整個網際網路上路由。第二,它本應是全球唯一的,因為各家製造商都領到互不相同的號段——不過現代手機如今會為了隱私而隨機化它。
從共用匯流排到交換器:碰撞逐漸消失
這裡有個圓滿的結局,以及另一個誠實的轉折。上面的一切都假設許多站台共用一個會碰撞的媒介——一個 碰撞網域,也就是其發送會彼此相撞的那組裝置。在共用纜線的古老乙太網路或一台笨集線器上,這確實成立。但現代有線乙太網路是透過一台 交換器運行的,它給每個裝置一條專屬、全雙工的連結——一條你能同時收發的私人雙線道。在這樣的連結上,沒有任何人能與你相撞,所以 CSMA/CD 無事可做。它為了向後相容仍留在標準裡,但在交換式網路上,它基本上從不觸發。
交換器怎麼知道哪個連接埠通往哪個 MAC?它靠觀察來學習——就像一位學會了每個人座位在哪的接待員。每當一個訊框抵達,交換器就記下來源 MAC 以及它進來的那個連接埠,把這筆資料存進一張表;此後,要送往那個 MAC 的訊框,就只從相符的連接埠送出,而不再對所有人大喊。於是一台交換器,把一個大碰撞網域切成許多個小碰撞網域,一條連結一個。但要當心那個經典的考試陷阱:交換器並不會切斷廣播的觸及範圍——送往 FF:FF:FF:FF:FF:FF 的訊框,仍會淹沒每一個連接埠。要切開那個廣播網域,得靠一台路由器或一個 VLAN,而不是一台單純的第二層交換器。
退一步,欣賞一下資料連結層在這一層裡完成的事。它替位元畫出界線、做成訊框;用檢查碼或 CRC 抓出毀損;用 ARQ 與滑動視窗救回丟失的訊框;用多重存取規則決定誰能開口;並用 MAC 位址替鄰居命名。有了這些,跨越單一一條連結的那一跳,就能可靠地運作。下一層將完全離開單一的線路:一個封包要如何跨越許多跳、穿過它從未見過的網路,用上那種 MAC 位址刻意不具備的階層式位址,找到自己的路?那就是網路層,也正是網際網路真正成為網際網路的地方。