前言
Socket 是什麼?
Socket 執行流程
#Socket TCP 是如何建立連線的
#Socket TCP 是如何斷開連線的
#前言
一說到網絡,大家必然會想到TCP、UDP、Http、三握四揮等,但是一說Socket,大家可能會有點模糊了,只知道網絡中會用到,但是Socket 究竟是什麼? 套接字又是啥?為啥網路離不開 Socket?
Socket 是什麼?
Socket 其實就是套接字,大部分人對於Socket 的理解就是它可以實現一個簡單的網路通信,但是它「具體解決了哪些問題?有什麼實際的作用?為什麼會有一個Socket 出現?」
#Socket 其實是在「應用層與傳輸層之間的一個產物」,它把傳輸層的很多複雜操作封裝成一些簡單的接口,來讓應用層調用以此來實現進程在網絡中的通信,Socket 是對端口通信開發的工具,它要更底層一些。
Socket 其實類似一台洗碗機,它的功能就是洗碗(網路通訊),如果沒有它,你可能需要自己手動去洗碗(手動呼叫傳輸層、應用層之間的各個api),但是有了它你只需要點擊開關、調整時長就行了(封裝了api),你可以不需要它,但是如果沒有它,洗碗(應用層與傳輸層之間的交互)將變得非常繁瑣。
一次完整的網路通訊必不可少的會經過實體傳輸層的網路線和網路卡,網路傳輸層的IP 協定可以知道要傳送資料給哪台機器,但是在電腦系統中會運行不同進程,那要如何把「網卡中的網路資料識別出來是給哪個進程的」,這其實就是Socket 設計的想解決的一點了。
Socket 是「對 TCP/IP 或 UDP/IP 協定的封裝」,Socket 本身其實就是一個呼叫介面。透過這個介面我們在開發網路應用程式的時候,就可以不用關心底層是怎麼實現的,減輕開發的難度。
Socket 執行流程
基於TCP
##Server
- socket():表示建立一個socket,底層會產生一個檔案描述符,用來表示該socket
- #bind():用來綁定服務的端口,位址,這裡一般都是以固定的為主,因為在客戶端連接的時候需要指定
- listen():當綁定完成之後,listen 就會監聽這個連接埠的封包
- accept():相當於一個開關,表示我準備好了,可以接受請求了,但這裡會一直阻塞,直到客戶端連線成功
- read():讀取客戶端發送過來的內容
- write ():客戶端寫入要傳回的資料
- close():斷開連接,
Client
- socket():表示建立一個socket,底層會產生一個檔案描述符,用來表示該socket
- connet():表示與指定地址連接,在此之前,會隨機創建自己的端口,tcp 的
- write():客戶端寫入要傳送的資料
- read():客戶端讀取服務端傳回的資料
- close():斷開連接,#
基於UDP
#這裡我就不細寫了,其實大同小異,從流程圖上就可以看到
因為UDP 是無狀態的,所以對於服務端來說沒有連接,並且其會在調用Recvfrom() 方法後就收客戶端的請求,並一直阻塞,直到收到訊息
Socket TCP 是如何建立連線的
在Socket 綁定完伺服器的位址後,就開始跟伺服器建立連線了,TCP 建立連線的方式其實就是大名鼎鼎三次握手了
- 第一次握手:A 的 TCP 程序建立一個 傳輸控制塊 TCB ,然後向 B 發出連線請求封包。之後將同步位元 SYN 設定為 1,同時選擇一個初始序號 seq=x,這時客戶端 A 進入到 SYN-SENT(同步已傳送)狀態。
- 第二次握手:B 收到連接請求訊息段,如果同意建立連接,則向 A 發送確認。在確認封包段中 同步位元 SYN=1、確認位元 ACK=1、確認號碼 ack=x 1,同時也為自己選擇一個初始序號 seq=y,這時伺服器 B 進入 SYN-RCVID 狀態。
- 第三次握手:A 收到 B 的確認以後,再向 B 發出確認。確認封包 ACK=1、確認號碼ack=y 1。這時A進入到 ESTAB-LISHED 狀態。當B接收到A的確認後,也進入 ESTAB-LISHED 狀態。連線建立完成
三次握手發生在socket 的哪幾個函數中
- 當客戶端呼叫connect 時,觸發了連線請求,向伺服器發送了SYN 訊號,這時connect 進入阻塞狀態;
- 伺服器監聽到連接請求,即收到SYN,呼叫accept 函數接收,進入阻塞狀態,在此之前會盡力socket、bind、listen 函數;然後返回相關的syn 以及ack 信號
- #客戶端接受到服務端的訊息,此時connect 完成,解除阻塞狀態,並且向服務端發送ack 訊號
- 服務端收到ack,accept 阻塞解除,完成連線
#在建立連線之後,connect() 就已經執行完畢了,服務端就可以傳送資料到客戶端了。
Socket TCP 是如何斷開連線的
- #第一次揮手:A 先發連線釋放封包段,段首部的終止控制位FIN=1,序號seq=u(等於A前面發送資料的最後一個序號加1);然後A 進入FIN-WAIT-1(終止等待1)狀態,等待B 的確認。
- 第二次揮手:B 收到A 的連線釋放封包段後,立刻發出確認封包段,確認號ack=u 1,序號seq=v(等於B前面發送資料的最後一個序號加1);然後B 進入CLOSE-WAIT(關閉等待)狀態。
- 第三次揮手:A 收到B 的確認報文段後進入到FIN-WAIT-2(終止等待2)狀態,繼續等待B 發出連線釋放報文段;
- 若B 已經沒有資料要傳送,B 就會向A 發送連線釋放封包段,段首部的終止控制位元FIN=1,序號seq=w (半關閉狀態可能又發送了一些資料),確認號ack=u 1,這時B進入LAST-ACK(最後確認)狀態,等待A的確認。
- 第四次揮手:A收到B的連線釋放封包並發出確認,確認段落中確認位ACK=1,確認號碼ack=w 1,序號seq=u 1;然後A 進入到TIME-WAIT(時間等待)狀態。當 B 再接收到該確認段後,B 就進入 CLOSED狀態。
第四次揮手後為何要等待2MSL
首先2MSL 的時間是從客戶端(A)接收到FIN 後發送ACK 開始計時的。如果在TIME-WAIT 時間內,因為客戶端(A)的ACK 沒有傳送到服務端(B),客戶端(A)又接收到了服務端(B)重發的FIN 封包,那麼2MSL 時間會被重置。等待2MSL 原因如下
- 如果B沒有收到自己的ACK,會逾時重送FiN那麼A再次接到重送的FIN,會再發送ACK
#在最後一次揮手後A 並不知道B 是否接到自己的訊息,
包括ACK 是以上哪兩種情況,A 都需要等待,要取這「兩種情況等待時間的最大值,以應對最壞的情況發生」,這個最壞情況是:去向ACK訊息最大存活時間(MSL) 來向FIN訊息的最大存活時間(MSL)。這剛好是2MSL,這個時間,足以使得原來連線的資料包在網路中消失。
因為這個ACK 是可能遺失的,會導致伺服器收不到對FIN-ACK 確認封包。假設客戶端不等待 2MSL ,而是在發送完 ACK 之後直接釋放關閉,一但這個 ACK 遺失的話,伺服器就無法正常的進入關閉連線狀態。
以上是面試官:Socket TCP 是如何斷開連線的?的詳細內容。更多資訊請關注PHP中文網其他相關文章!