網路程式設計基礎知識
女主宣言
OpsDev團隊自從九月中旬開啟技術分享活動以來,受到了廣大技術愛好者的好評,收效顯著。第一期公開課由李鋼老師開講,內容為“應用程式設計基礎課”,分為5節課,本文為本期的第二節課,重點講解網路基礎程式設計,包括網路結構、TCP程式設計模型和IO模型。現整理成文分享出來供大家一起學習。
PS:豐富的一線技術、多元化的表現形式,盡在“ HULK一線技術雜談 ”,點關注哦!
本人從事linux下web程式設計多年,最近有幸給組內同學做培訓,希望能夠給大家介紹下自己這些年在應用程式設計方面的經驗,今天給大家介紹以下網路程式設計方面需要掌握的基礎知識。
1
網路分層模型
先看一張圖:
2
TCP服務端和客戶端程式設計模型
TCP 連線建立和斷開
TCP建立連線需要三次握手,而斷開連線需要四次揮手,如圖:
這張圖清晰的說明了連線的建立、資料傳送以及斷開連線時所對應的程式設計函式,另外還有相應的TCP狀態轉換。
服務端客戶端程式設計函式
由此可見,服務端程式設計用到的主要函式為:
-
socket:建立一個socket,返回的檔案描述符fd之後用於bind和listen
-
bind:繫結socket和ip+port
-
listen:呼叫後,服務端狀態變為LISTEN,可以接收網路連線
-
accept:函式在連線建立後返回一個connfd,對這個檔案描述符的讀寫就是在做網路接收和傳送
-
read:網路對端傳送來的資料會放到核心的接收緩衝區,read就是從這個緩衝區中讀取資料到應用程式
-
write:應用程式要傳送資料到網路對端時,呼叫此函式,會現將資料寫到核心的傳送緩衝區中,之後核心會負責將資料傳送給網路對端
-
close:關閉連線
關於服務端的用於連線的fd和用於讀寫的fd,請見下圖:
客戶端程式設計用到的函式為:
-
socket:建立一個socket,之後用於連線伺服器,做資料讀寫用
-
connect:發起到服務端的連結,返回時TCP三次握手完成
-
write:同服務端的write
-
read:同服務端的read
-
close:關閉連線
幾個概念
backlog
核心中會維護兩個佇列:
-
未完成連線的佇列: 服務端收到客戶端的連線請求(SYNC),在三次握手完成前,會放到這個佇列中
-
完成連線的佇列:完成三次握手後就建立了一個TCP連線,這個連線會放到這個佇列中
linux的man listen中說:
The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow
我理解,backlog定義了未完成連線的佇列的最大長度
RTT
這個概念常常聽到,請見圖:
RTT的定義是Round-Trip Time,即資料包的往返時延
TCP Stream
我們通常說TCP是流式的,這是什麼樣的概念呢?
對應一個TCP連線,核心會給這個連線分配一個傳送緩衝和接收緩衝,我們的應用程式對這來兩個緩衝區的讀寫就是在做網路資料的接收和傳送。
而資料在網路上的傳輸是核心自己在維護的,當傳送緩衝區中有資料後,核心就把這些資料傳送給對端;同樣的,當對端有資料過來時,核心會把它放到接收緩衝區中,等待應用程式的讀寫。
這樣的傳送和接收資料的過程,就像水流一樣,所以我們說TCP是流式的。
TCP狀態轉換
TCP定義了很多狀態,這些狀態之間的轉換關係如下圖:
這些狀態都記住有難度,需要時查下就好了。
3
IO模型
網路操作就是IO操作,而且網路的IO是最慢的一種了,網路程式設計的很大難點就是妥善的處理好這一塊的問題。
Unix定義了多種IO操作模型,分別是:
-
阻塞IO
-
非阻塞IO
-
IO多路複用
-
訊號驅動IO
-
非同步IO
阻塞IO
這裡有一點非常重要的概念要先說明下,那就是阻塞的是什麼?
首先要記住:資料在網路上的傳輸完全是核心在控制的,應用程式中的read和write只是在讀寫接收緩衝和傳送緩衝。
讀阻塞:呼叫read時,如果接收緩衝區中沒有資料,那麼就會產生阻塞
寫阻塞:呼叫write時,如果傳送緩衝區中的資料沒有傳送出去,那麼就會產生阻塞
那麼如果沒有產生阻塞,那麼兩者的執行時間為:
read的執行時間為:將核心接收緩衝區的內容拷貝到使用者空間中的應用程式緩衝區
write的執行時間為:將使用者空間中的應用程式緩衝區的內容拷貝到核心的傳送緩衝區
非阻塞IO
理解了導致阻塞的原因,那麼非阻塞就非常好理解了:
當核心緩衝區無法讀寫時,read和write就會返回EWOULDBLOCK,應用程式就需要過會兒再來操作。
光是這樣,還不能實現高效能的網路程式,這是因為我們無法判斷應該什麼時間再來做讀寫操作。
如果一直迴圈讀寫,那麼CPU佔用會很居高不下;如果sleep一段時間,那麼多長時間合適呢?
所以,如果想開發高效能的網路程式,我們還需要別的武器。
IO多路複用
這是作業系統提供的一種通知機制,告訴應用程式何時可以做讀寫操作:
不同作業系統提供了不同的程式設計介面,一個非常有名的庫libevent就是對這些庫的一個統一介面封裝。
IO多路複用也是現在用的最多的一種高效能網路伺服器的IO處理模型,例如Nginx
訊號驅動IO
不同於IO多路複用,作業系統用訊號的方式告訴應用程式何時可以做讀寫操作:
非同步IO
最後這一種我沒有用過,從概念上理解,相當於作業系統將資料做完使用者空間和核心空間的複製後,才會通知應用程式:
4
面對如此眾多的伺服器
上面這些,都是筆者程式設計這些年,覺得非常受用的基礎知識。正確的認識這些知識,很多問題你都可以自己想明白了。
筆者也是在不斷學習中,如果有錯誤的地方,還望指正,我們共同進步,謝謝!
參考:
UNIX網路程式設計(卷1):https://book.douban.com/subject/4859464/
TCP/IP詳解(卷1):https://book.douban.com/subject/26790659/
Linux/UNIX系統程式設計手冊:https://book.douban.com/subject/25809330/
HULK一線技術雜談
由360雲平臺團隊打造的技術分享公眾號,內容涉及 雲端計算 、 資料庫 、 大資料 、 監控 、 泛前端 、 自動化測試 等眾多技術領域,通過夯實的技術積累和豐富的一線實戰經驗,為你帶來最有料的技術分享
