兩種連結,以及為什麼其中一種很難
在這個段落到目前為止,我們一直悄悄假設一條點對點連結:線的一端一個發送者、另一端一個接收者,沒有別人。在這種連結上,誰能說話這個問題根本不會出現,因為只有一個發話者。框架化、CRC、滑動視窗全都一跳一跳地直接運作。你家到電話交換局的那條電話線就是這樣。
但現實中極大一部分的連結是廣播連結:一個共用媒介,許多機器都接上去,其中一台機器發出的訊號會傳到所有人那裡。早期跑在共用同軸電纜上的乙太網路正是如此。Wi-Fi 也是,無線存取點周圍的空氣就是大家共同呼吸的同一條通道。想像一間沒有主席的安靜會議室:任意多人都可以發言,但只要兩人同時開口,兩種聲音就糊成一團噪音,誰也聽不懂。這團糊掉的東西叫做碰撞(collision),而它就是整個問題所在。
協調誰可以在共用媒介上發送,是 MAC 子層的工作,也就是資料連結層的下半部。媒介存取控制裡的 MAC 此刻還不是在講位址(那是第 5 篇的事),它是決定誰能存取通道的裁判。一套這種裁判規則就是一個多重存取協定,而幾十年來工程師為它找到了三種真正不同的哲學。
第一家族:切分通道,讓人永不碰撞
第一直覺是乾脆讓碰撞不可能發生:事先發給每個發話者一塊私有的通道切片。切法有兩種自然的方式。分時是給每個站台自己一個週期性重複的時槽,像輪流發言,就算你沒話說也照樣輪到你固定的那一輪。分頻是給每個站台自己一段頻段,像替每段對話指定一個不同的廣播電台,於是大家可以同時講而不會聽到彼此。這就是你在實體層那一段認識過的同一個分時與分頻多工概念,現在拿來在眾多發話者之間維持和平。
通道切分極為公平又完全無碰撞,這也正是為什麼它用在負載穩定又繁重的地方,例如電話交換機之間的骨幹中繼線。但對「爆量式」流量它有個致命缺陷。假設十個站台共用一條 10 Mbps 的通道,平分成十塊切片。每個站台永遠被封頂在 1 Mbps,即使另外九個都閒著也一樣。當一個站台在龜速爬行時,通道有十分之九空在那裡。對電腦來說——它們總是突然爆量、然後又沉默好久——這種浪費讓人無法忍受。
第二家族:直接開口,再從碰撞中復原
相反的哲學是隨機存取:完全不發切片,讓任何站台一有框架就以全速發送出去,並接受兩個框架偶爾會重疊而碰撞。這場賭注是:在爆量式流量下碰撞很少見,所以大多數時候一個站台能獨佔整條通道。隨機存取協定的工作,是在碰撞真的發生時該怎麼辦:偵測到它,並讓每個輸家各自等一段不同的隨機時間再重試,這樣它們才不會又步調一致地再撞一次。
所有隨機存取的祖宗是 ALOHA,於 1970 年代初打造,用無線電串起夏威夷群島。純 ALOHA 簡單到近乎魯莽:上層一交來一個框架,就立刻發送;如果你沒收到回應的確認,就假設它撞了,等一段隨機時間,再重送。不聽、不分槽、不協調。它能用,也教會了整個領域隨機存取的行為長什麼樣,但它的效率讓人謙卑。
Pure ALOHA -- a frame collides if ANY other frame starts during a 2-frame window: ...|---- frame X ----|... ...........|---- frame Y ----|... <- overlap! both X and Y are lost Best-case channel use: Pure ALOHA ~ 18% (vulnerable window = 2 frame times) Slotted ALOHA ~ 37% (force starts onto slot edges -> window = 1 frame time)
為什麼這麼低?一個框架只要在它前後共兩個框架時間的窗口內,有任何別的框架開始,它就毀了:前一個框架時間(別人已先開始,你撞上去)和後一個框架時間(別人在你結束前才開始)。分槽 ALOHA 多加一條規則,強迫每次發送只能在一個共用時槽的邊緣開始。這就拿掉了那半個「別人能在你發送中途插進來」的窗口,把危險區縮成一個框架時間,並讓效率大致翻倍到約 37%。下一篇的 CSMA 透過再加一個謙遜的習慣推得更高:開口前先聽。
第三家族:靠傳遞發言權來輪流
切分公平,但閒置時浪費;隨機存取在安靜時有效率,卻在重負載下崩潰——因為通道越忙,大家就越常碰撞。第三個家族,輪流,試圖兼得兩者之長:在只有一個站台忙碌時像隨機存取一樣有效率,在大家都忙碌時又像切分一樣無碰撞且公平。它的點子是讓站台彼此傳遞一個唯一的發言權,只有持有者才可以說話。
它有兩種形態。在輪詢(polling)中,一個主節點輪流邀請每個站台,像老師繞著桌子逐一問每個孩子有沒有要補充的;沒東西要送的站台會被很快跳過。在權杖傳遞(token passing)中,根本沒有主節點:一個叫做權杖的小小控制框架不斷循環,你只有在持有權杖時才可以發送,發完就把它傳下去。Token Ring 與 FDDI 就是這樣運作的。下面的步驟讓權杖版本具體起來。
- 一個權杖框架沿固定順序在各站台間循環,像接力棒在圓圈裡一手傳一手。
- 沒東西要送的站台,在權杖一抵達的瞬間就直接把它轉交給鄰居,於是閒置的環轉得很快、幾乎不浪費。
- 真的有框架要送的站台會抓住權杖,發送它的框架(此刻它獨佔整條通道、零碰撞風險),然後把權杖放開、傳給下一個。
- 因為只有權杖持有者可以說話,兩個框架永遠不會重疊,所以效率一路維持到滿載都很高,而不會崩潰。
選擇一個家族,以及關於無線的誠實提醒
退一步看,這三個家族依照它們各自要優化的目標排成一列。通道切分無碰撞又公平,在穩定、繁重的負載下最理想,但流量爆量時浪費。隨機存取讓幸運的站台拿到整條通道,低負載時效率出色,但重負載下碰撞堆積就會劣化。輪流是謹慎的折衷:無碰撞、公平、整個範圍都有效率,代價是額外負擔,以及對權杖或主節點脆弱的依賴。沒有哪個家族在所有地方都最好;對的選擇,是那個與流量真實行為相符的選擇。
進入下一篇前,先給一個誠實的警告。隨機存取倚賴站台察覺碰撞的能力,而在有線上這很容易:發送端可以聽自己的訊號、聽見那團糊掉的東西,這就是有線乙太網路的「CSMA 加碰撞偵測」運作的方式。但無線電做不到。當一張 Wi-Fi 卡正在發送時,它自己送出的訊號比任何進來的訊號強上幾百萬倍,所以它實際上是聾的,根本聽不到碰撞正在發生。正是這個單一的物理事實,讓 Wi-Fi 採用碰撞「避免」而非「偵測」,而這也是下一篇要解開的第一個結。