在Stanford CS144的課程實驗Lab0~Lab4中,我們動手實現了一個自己的TCP協議,並且能夠真的與互聯網通信!此外,感謝Stanford開源本實驗並提供了大量的優質測試用例,使得我們僅僅通過互聯網就能獲取到這么好的學習資源。
本篇博客將從我自己的角度出發簡單介紹TCP協議,闡述實現的難點以及在實驗過程中的收獲。
什么是TCP?
正式的定義以及計網相關的基礎知識請讀者自行stfw,在此我只簡單地從自己角度闡述。
- 從網絡協議抽象層來看,TCP是一個傳輸層協議,用於實現不同主機上進程與進程之間的通信。
- 從TCP提供的服務來看,TCP是可靠數據傳輸協議,保證將輸入的數據保序、保真、不丟失地輸出到指定位置。
- 從TCP的核心思想來看,TCP依托於底層不可靠的網絡層協議來實現可靠傳輸,其中的很多設計具有深刻內涵。
TCP的設計有哪些思想?
作為對本課程實驗設計者的尊重,本篇博客不會涉及具體的思路,更不會展示代碼。我只想聊一聊主要方向。
在深入TCP細節之前,讓我們來做一個簡單的思維實驗:雙軍問題
在一場攻城戰中,己方的兩位將軍只能派遣信使穿越敵方領土來相互交流。信使可能被逮捕,但他們想要確定一個一起進攻的時間。請問兩位將軍采取什么策略才能確定一個100%兩人都會進攻的時間呢?
將軍\(A\)派遣信使傳遞消息\(M_1\),將軍\(B\)收到后傳遞\(M_2\)回復。為了使得\(B\)確信\(M_2\)被收到,\(A\)又需要發送\(M_3\)....
可以證明,這個問題是無解的。
其實,TCP要解決的就是這樣的難題。網絡層協議是不可靠的,TCP為了確認數據可靠到達,不得不采取回復機制,即發送方只有正確收到接收方的回復才能確認該數據送達。但回復機制最終還是要面臨上述難題————TCP的兩端必須確保自己、對方都希望終止連接才能退出。
為了解決這一難題,TCP采用了工程化的辦法將誤判的概率降到極低。如下圖所示,最后一個報文的發送方會等待一段時間(linger time
). 如果這段時間里對方沒有發送要求重傳的報文則默認對方已收到報文。
思考:如上圖所示,先接收到結束信號的主機可以不在最后等一段時間,為什么?
除了結束時的確認機制外,TCP還有很多機制確保數據可靠傳輸同時保證性能:
- 如上文所說的,為了確認單個報文正確送達引入應答機制
- 為了確保報文之間有序引入計數器機制
- 為了提高效率而不是“一問一答”,引入流水線機制,並通過滑動窗口、回退等技巧保證正確性
- 為了辨別報文丟失和網絡擁堵,引入計時機制,同時為了不斷逼近當前網絡擁堵情況采用指數上升、線性下降法動態調整計時。
......
TCP是一個經典且有效的協議,其設計者早在如今的因特網普及之前就提出了相關思想並因此獲得圖靈獎。我們在學習網絡協議的時候,更重要的是學習這個系統的抽象機制以及各抽象層的設計思想,TCP正是一個巨大的思想寶庫。但由於其過於經典,關於它的文章、教材數不勝數,我就不在此一一贅述上述機制的細節了,感興趣的讀者可以自行學習。
CS144如何組織實驗?
國內高校的一些課程最不合理的一點就是學習與實踐分離,實踐與現實疏遠。下面我很主觀地做一些對比:
課程主要目標:學習C語言程序是怎樣最終運行起來的?
- A課程:動手寫一個計算機系統模擬器,設計指令集、運行環境、簡易操作系統,並最終能運行真實的程序甚至仙劍奇俠傳;動手能力強的同學甚至可以用不同方式實現同一抽象層。實踐占評分大部分。
- B課程:只有理論課沒有實驗作業,教材提供的現成模擬器運行的是自創的及其簡易的指令集,只能完成一些算術運算操作。期末考背書題占比極高。
課程主要目標:學習計算機硬件組成及設計
- A課程:動手寫一個芯片!如果順利還能成功流片拿到實物。
- B課程:不依托具體指令集講一些計算機組成中抽象的概念,作業考試大部分都是計算讀寫速率,平時實驗雖然很貼合課程內容,但寫成之后唯一的作用就是通過測試用例。
根據我的調查,美國很多大學的課程及其注重實踐。比如Stanford CS144學網絡,8個Lab就是實現各種網絡協議,能夠真正和互聯網通信;CMU 15-445學數據庫設計,Lab就是實現一個完整的的數據庫管理系統並管理兩百多萬條數據......
誠然,這樣的實驗需要大量的精力去設計,但其對於學生的教育和鼓舞也是極大的。幸運的是國內也有很多優秀的人才做出了這方面的貢獻(南大ics、os,一生一芯等)。作為一個資質平平的學生,我可能沒有能力為國內計算機教育事業貢獻自己,但我希望能盡自己的努力讓身邊的同學知道有更好的課程,計算機教育不只是自學,不只是背書,不只是調包。
下面回到正題,CS144是怎么把這個實驗組織起來使得學生既不需要關注與OS、硬件等交互的細節,又能真正寫出一個work的程序的呢?
這張圖是他們的實驗組成。
在Lab0中,我們調用Linux的TCPSocket實現了自己的wget程序
Lab1~Lab4中,我們實現了自己的TCPConnection並最終替換了上面的Socket使得Lab0中的wget運行在自己的TCP上與互聯網連接。
CS144把TCP的實現分為兩大部分,Socket和Connection,其中Socket由框架代碼直接給出(主要涉及與底層、OS交互,不是TCP的重點),Connection又分為Sender、Receiver兩部分讓同學分時完成。
這樣做一方面降低了實驗難度,另一方面也保證了實驗成果的有用性。
除此之外,CS144開源了大量的測試用例,其測試腳本編寫的也及其易懂、好用,很值得學習。
CS144提供的測試用例覆蓋度及其廣泛,這在一定程度上確保了自己實現的TCP的魯棒性。每個實驗都會用到之前的代碼,很多之前已經通過的代碼之后還會找出BUG。在完成Lab4后,大量的測試用例模擬了真實網絡環境中的各種情境,如果沒有真正理解TCP的每一處細節,很難通過。最后,在自己寫的協議上與互聯網通信,既是一種收獲,也是一次測試(是的,即使通過了所有用例還可能會出現新的bug).
總而言之,CS144通過合理的抽象讓學生關注於任務的重心,通過划分降低實驗難度,提供大量用例提升實驗質量,最終成果也讓人頗感欣慰。
總結
- 這幾個實驗其實代碼量並不大(一共不超過500行),但花了我三四天的時間。現在想來,還是沒有在一開始理解TCP的很多細節,做的時候存在大量的面向用例編程行為。
- 這次實驗再次讓我明白了基礎設施的重要性(看來PA還是沒吃夠苦頭),gdb、wireshark等設施也是后面printf大法不管用才想起來。
- 通過這次實驗,我更堅定了自己學以致用的學習策略。即學到了什么通過實踐檢驗,學得怎么樣通過實踐效果評判,不讀死書。因此之后可能會開數據庫的新坑...
- 再次感受到自己能力的不足...還需要再努力尋找更好的學習方法呀
我的水平有限,以上各部分內容難免有疏漏、主觀臆斷的部分,如有錯誤、冒犯請指出,感激不盡