TCP/IP協議是個協議族,包含各種協議這裡不再敘述,主要分為鏈路層、網路層、傳輸層、應用層。這裡主要講一下面向連接過程的傳輸層TCP協議。它的報文結構如下圖:
粗體關鍵欄位比較重要
下圖為客戶端和服務端三次握手的過程:
關於伯克利套接字的說明
我們在Linux類操作系統環境下實現網路通信通常會使用伯克利套接字。伯克利套接字(Berkeley sockets),也稱為BSD Socket。伯克利套接字的應用編程介面(API)是採用C語言的進程間通信的庫,經常用在計算機網路間的通信。 BSD Socket的應用編程介面已經是網路套接字的事實上的抽象標準。大多數其他程序語言使用一種相似的編程介面。BSD Socket作為一種API,允許不同主機或者同一個計算機上的不同進程之間的通信。它支持多種I/O設備和驅動,但是具體的實現是依賴操作系統的。這種介面對於TCP/IP是必不可少的,所以是互聯網的基礎技術之一。它最初是由加州伯克利大學為Unix系統開發出來的。所有現代的操作系統都都實現了伯克利套接字介面,因為它已經是連接互聯網的標準介面了。
和連接建立相關的API有:connect, listen, accept3個,connect用在客戶端,另外2個用在服務端。對於TCP/IP protocol stack來說,我們這裡只討論這3個應用層的API幹了什麼事情。
connect:
發送了一個SYN,收到Server的SYN+ACK後,代表連接完成。發送最後一個ACK是protocol stack,tcp_out完成的。
listen
在Server這端,準備了一個未完成的連接隊列,保存只收到SYN_C的socket結構;還準備了已完成的連接隊列,保存了收到了最後一個ACK的socket結構。
accept
應用進程調用accept的時候,就是去檢查上面說的已完成的連接隊列,如果隊列里有連接,就返回這個連接;空的,blocking方試調用,就睡眠等待,nonblocking方式調用,就直接返回,一般一"EWOULDBLOCK「 errno告訴調用者,連接隊列是空的。
而四次揮手對應的socket api為:
主要為close(fd),剩下的確認等待等操作會交給內核。
我們要知道網路傳輸是有延遲的,可能丟失的,不是說A發一個包給B,B保證能立刻收到,甚至B可能一直收不到。
? 第一次。A跟B說,我要建立連接了。
? 第二次。B跟A說,OK,那我也建立連接。
? 第三次。A跟B說,嗯,我知道了。
第二次和第三次都是為了保證連接是可靠的。
? 假設只有一次握手,而A的包無法發到B那裡去,那A就是自顧自的建立了連接,傻傻的發信息,卻不知道對方其實根本收不到。所以第二次握手是為了告訴A,B收到了你的信息。假設只有兩次握手,那麼對B來說,B是不知道A是否收到了自己的信息的,第三次握手是為了告訴B,A收到了B的信息了,並且可以互發信息了。 B真的需要知道 A是否收到了自己發出的第二次握手的信息嗎?是的,如果A的第一次握手因為某些原因延遲很久才到B,而其實現在A和B已經聊完天,關閉連接了,這時B發出第二次握手,而A已經沒什麼想跟B說的,就不會發出第三次握手,這樣B就不會建立連接消耗資源。若是只有二次握手的這種情況,B會直接建立連接消耗資源。
第一次。A跟B說,我要斷開連接了。
第二次。B跟A說,好的,我知道了,我不再接收你的信息了。
第三次。B跟A說,我傳給你的信息傳完了,你可以關閉連接了。
第四次。A跟B說,好的,我關閉連接了。
第二次揮手是為了告訴A,B知道你不再發送信息了,且B不再接收信息。但B仍可以向A發送信息。因為A是主動關閉的一方,但B可能仍然有信息未發送完。第三次揮手是為了告訴A,B的信息發完了,A你可以關閉連接了。 第四次揮手是為了告訴B,A我知道可以關閉連接了,你也可以關閉了。對B來說,第四次揮手B才知道A成功關閉連接了,不關閉很耗費資源,所以要保證關閉了。若B接收不到第四次揮手信息,將會繼續第三次揮手,直到收到確認信息為止。注意:對A來說,第四次揮手後2MSL內未接收到B的第三次揮手信息才會關閉連接,否則會繼續第四次揮手。防止B接收不到第四次揮手信息。(若B接收不到第四次揮手信息,將重複發送第三次揮手信息,這個2MSL就是如果真的有重複發送的第三次揮手信息,在這個時間內肯定到達A了(路由不出問題的話),就是為了保證B收到第四次揮手信息)。
關於一個很出名的狀態圖附上:
客戶端的狀態變遷:CLOSED-->SYN_SENT-->ESTABLISHED-->FIN_WAIT_1-->FIN_WAIT_2-->TIME_WAIT-->CLOSED
伺服器的狀態變遷:CLOSED-->LISTEN-->SYN_RCVD-->ESTABLISHED-->CLOSE_WAIT-->LAST_ACK--->CLOSED
CLOSED:這個狀態不是一個真正的狀態,是圖中假想的一個起點或者是終點
LISTEN: 伺服器等待連接過來的狀態
SYN_SENT: 客戶端發起連接(主動打開),變成此狀態,如果SYN超時,或者伺服器不存在直接CLOSED
SYN_RCVD:伺服器收到SYN包的時候,就變成此狀態,
ESTABLISHED:完成三次握手,進入連接建立狀態,說明此時可以進行數據傳輸了
FIN_WAIT_1:客戶端執行主動關閉,發送完FIN包之後便進入FIN_WAIT_1狀態
FIN_WAIT_2:客戶端發送FIN包之後,收到ACK,即進入此狀態,其實就是半關閉的狀態
TIME_WAIT:這個狀態從圖上看,有3中情況,從FIN_WAIT_2進入,客戶端收到伺服器發送過來的FIN包之後進入TIME_WAIT狀態,有CLOSING狀態進入,這是同時關閉的狀態,同時發起FIN請求,同時接收並做了ACK的回復,從FIN_WAIT_1進入,收到對端的FIN,ACK,並回復ACK,這個地方感覺是,FIN和ACK是一塊來的.
CLOSE_WAIT:接收到FIN之後,被動的一方進入此狀態,並回復ACK
LAST_ACK:被動的一端發送FIN包之後 處於LAST_ACK狀態