動手學TCP——CS144實驗感想

xheiyan發表於2022-03-08

在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). 如果這段時間裡對方沒有傳送要求重傳的報文則預設對方已收到報文。

image

思考:如上圖所示,先接收到結束訊號的主機可以不在最後等一段時間,為什麼?

除了結束時的確認機制外,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大法不管用才想起來。
  • 通過這次實驗,我更堅定了自己學以致用的學習策略。即學到了什麼通過實踐檢驗,學得怎麼樣通過實踐效果評判,不讀死書。因此之後可能會開資料庫的新坑...
  • 再次感受到自己能力的不足...還需要再努力尋找更好的學習方法呀

我的水平有限,以上各部分內容難免有疏漏、主觀臆斷的部分,如有錯誤、冒犯請指出,感激不盡

相關文章