Socket和TCP的對(duì)應(yīng)關(guān)系

前言

??使用TCP/IP網(wǎng)絡(luò)協(xié)議時(shí),一般都是基于Socket的API進(jìn)行網(wǎng)絡(luò)編程。應(yīng)用程序通過Socket接口和內(nèi)核交互,內(nèi)核維護(hù)TCP/IP協(xié)議的具體通信過程。那么Socket的API具體是如何和TCP/IP協(xié)議棧對(duì)應(yīng)呢?

連接

??建立連接相關(guān)的API有connect,listen,accept。服務(wù)端使用listen,accept等待連接,客戶端使用connect進(jìn)行連接。
??當(dāng)服務(wù)端調(diào)用listen后,會(huì)向系統(tǒng)內(nèi)核注冊Syn隊(duì)列和Accept隊(duì)列,接下來調(diào)用accept后會(huì)監(jiān)聽Accept隊(duì)列。此時(shí)如果客戶端調(diào)用connect向服務(wù)端發(fā)出連接請(qǐng)求,就會(huì)發(fā)生傳說中大名鼎鼎的三次握手過程。

  1. 客戶端內(nèi)核向服務(wù)端內(nèi)核發(fā)出SYN網(wǎng)絡(luò)包;
  2. 服務(wù)端內(nèi)核在Syn隊(duì)列插入一個(gè)節(jié)點(diǎn),并且向客戶端內(nèi)核發(fā)送SYN+ACK網(wǎng)絡(luò)包;
  3. 客戶端內(nèi)核發(fā)送ACK網(wǎng)絡(luò)包,此時(shí)客戶端的connect調(diào)用結(jié)束并返回,服務(wù)端內(nèi)核如果收到這個(gè)ACK網(wǎng)絡(luò)包,會(huì)從Syn隊(duì)列中刪除節(jié)點(diǎn)并在Accept隊(duì)列中增加節(jié)點(diǎn),此時(shí)服務(wù)端的accept調(diào)用就會(huì)監(jiān)聽到Accept隊(duì)列的節(jié)點(diǎn),并且返回。

??斷開連接的API主要就是close方法。當(dāng)任意一方調(diào)用close方法時(shí),就會(huì)發(fā)生傳說中的四次揮手過程。當(dāng)一方調(diào)用close時(shí),對(duì)方就會(huì)產(chǎn)生一個(gè)讀事件,當(dāng)讀到的數(shù)據(jù)是0時(shí),就知道對(duì)方關(guān)閉了連接。

  1. 主動(dòng)關(guān)閉方發(fā)送FIN包,然后進(jìn)入FIN_WAIT_1狀態(tài);
  2. 被動(dòng)方收到FIN包以后,發(fā)送確認(rèn)的ACK包,進(jìn)入CLOSE_WAIT狀態(tài),主動(dòng)斷開方狀態(tài)變成FIN_WAIT_2狀態(tài);
  3. 被動(dòng)方發(fā)送FIN包表示本方斷開連接,狀態(tài)變成LAST_ACK狀態(tài);
  4. 主動(dòng)關(guān)閉方接收到FIN包以后,發(fā)送ACK包確認(rèn),狀態(tài)進(jìn)入TIME_WAIT狀態(tài),被動(dòng)關(guān)閉方變成CLOSED狀態(tài)。

發(fā)送和接收

??通信過程使用send和recv方法。發(fā)送數(shù)據(jù)時(shí)使用send方法,send會(huì)將應(yīng)用需要發(fā)送的數(shù)據(jù)拷貝到內(nèi)核的發(fā)送緩沖區(qū);接受數(shù)據(jù)時(shí)使用recv方法,recv會(huì)從內(nèi)核緩沖區(qū)拷貝數(shù)據(jù)到應(yīng)用進(jìn)行處理。
??在阻塞模式下調(diào)用send時(shí),如果send的數(shù)量小于緩沖區(qū)的大小,會(huì)將應(yīng)用數(shù)據(jù)拷貝到內(nèi)核以后立即返回,如果send的數(shù)量大于緩沖區(qū)的大小,應(yīng)用會(huì)等待內(nèi)核將一部分?jǐn)?shù)據(jù)發(fā)送成功清除緩沖區(qū)再拷貝,直到所有的數(shù)據(jù)都拷貝到內(nèi)核以后才返回。
??在非阻塞模式下調(diào)用send時(shí),無論發(fā)送的數(shù)據(jù)是否大于內(nèi)核的緩沖區(qū),都會(huì)立即返回,返回的是成功拷貝的數(shù)據(jù)的數(shù)量。
??無論什么情況,應(yīng)用程序發(fā)送數(shù)據(jù)都只會(huì)將數(shù)據(jù)拷貝到內(nèi)核的緩沖區(qū),并不表示服務(wù)端成功接收了數(shù)據(jù)。
??在阻塞模式下調(diào)用recv時(shí),如果內(nèi)核緩沖區(qū)沒有數(shù)據(jù),會(huì)一直等待,直到讀取到數(shù)據(jù)。
??在非阻塞模式下調(diào)用recv時(shí),如果內(nèi)核緩沖區(qū)沒有數(shù)據(jù),會(huì)立即返回。

超時(shí)

  • 建立連接超時(shí)
    ??客戶端應(yīng)用在調(diào)用connect以后,內(nèi)核在發(fā)送SYN以后,在指定的超時(shí)時(shí)間內(nèi)沒有收到服務(wù)端返回的ACK網(wǎng)絡(luò)包。
  • 讀數(shù)據(jù)超時(shí)
    ??客戶端應(yīng)用在調(diào)用recv以后,在指定的超時(shí)時(shí)間內(nèi)沒有收到服務(wù)端返回的數(shù)據(jù)。
  • 寫數(shù)據(jù)超時(shí)
    ??客戶端內(nèi)核在發(fā)送外數(shù)據(jù)以后,由于網(wǎng)絡(luò)問題或者其它原因,導(dǎo)致服務(wù)端以后沒有返回確認(rèn)信息,此時(shí)系統(tǒng)內(nèi)核在重試多次以后關(guān)閉這個(gè)連接。此時(shí)如果客戶端應(yīng)用繼續(xù)調(diào)用send寫數(shù)據(jù),就會(huì)產(chǎn)生異常。

經(jīng)典問題

  • 關(guān)閉連接
    ??如果CLOSE_WAIT狀態(tài)大量堆積,說明被動(dòng)關(guān)閉方忘記調(diào)用close,此時(shí)被動(dòng)關(guān)閉方一直處于CLOSE_WAIT狀態(tài),久而久之服務(wù)器端的連接句柄就會(huì)被耗盡。當(dāng)然客戶端并不會(huì)一直處在FIN_WAIT_2狀態(tài),過一段時(shí)間以后,會(huì)自動(dòng)跳到下一個(gè)狀態(tài)。
    ??如果TIME_WAIT狀態(tài)大量堆積,這種情況一般屬于正?,F(xiàn)象,主動(dòng)關(guān)閉方在收到被動(dòng)關(guān)閉方的close請(qǐng)求以后,會(huì)進(jìn)入TIME_WAIT狀態(tài),并保持一定的時(shí)間。這是為了確保被動(dòng)關(guān)閉方?jīng)]有收到主動(dòng)關(guān)閉方發(fā)出的ACK包,此時(shí)被動(dòng)關(guān)閉方再次發(fā)送FIN時(shí),在這段時(shí)間內(nèi)主動(dòng)關(guān)閉方可以再次返回ACK包確認(rèn)。一般如果某臺(tái)機(jī)器短時(shí)間發(fā)出了大量的短連接請(qǐng)求(比如HTTP請(qǐng)求),就會(huì)出現(xiàn)很多的TIME_WAIT狀態(tài)。一般來說,如果出現(xiàn)大量的TIME_WAIT狀態(tài),服務(wù)器會(huì)配置成處于TIME_WAIT狀態(tài)的可復(fù)用。

  • 為什么握手只需要三次,揮手卻需要四次?
    ??客戶端在和服務(wù)端建立連接時(shí),服務(wù)端應(yīng)答以后肯定也需要和客戶端建立連接,所以可以把ACK和SYN包一起發(fā)送。但是斷開連接時(shí),被動(dòng)斷開的一方并不一定需要馬上斷開,可能還需要繼續(xù)發(fā)送數(shù)據(jù),所以此刻需要先發(fā)送ACK包確認(rèn)收到主動(dòng)斷開連接方的FIN包,然后在需要的時(shí)候發(fā)送FIN包表示本方也要斷開連接了。

  • RST包
    ??主動(dòng)關(guān)閉方發(fā)出的ACK如果被動(dòng)關(guān)閉方?jīng)]有收到,被動(dòng)關(guān)閉方再次發(fā)送FIN時(shí),如果主動(dòng)關(guān)閉方在TIME_WAIT狀態(tài)下,會(huì)再次發(fā)送ACK包;否者就會(huì)發(fā)出一個(gè)RST包。
    ??服務(wù)器返回了“RST”時(shí),如果此時(shí)客戶端正在從Socket套接字的輸出流中讀數(shù)據(jù)則會(huì)提示Connection reset”;服務(wù)器返回了“RST”時(shí),如果此時(shí)客戶端正在往Socket套接字的輸入流中寫數(shù)據(jù)則會(huì)提示“Connection reset by peer”。
    ??使用SO_LINGER時(shí),主動(dòng)關(guān)閉方可以直接關(guān)閉連接,避免關(guān)閉過程中的狀態(tài)過度。

  • 服務(wù)端掛了,客戶端還存在連接嗎?
    ??如果服務(wù)端是進(jìn)程掛了,系統(tǒng)會(huì)發(fā)送關(guān)閉報(bào)文,這種情況下客戶端連接會(huì)關(guān)閉。
    ??如果服務(wù)端是系統(tǒng)直接掛了,沒來的及發(fā)送關(guān)閉報(bào)文。
    ????1. 如果設(shè)置了keepalive,相當(dāng)于雙方會(huì)有心跳包,等到超時(shí)以后,客戶端就會(huì)關(guān)閉連接。
    ????2. 如果沒有設(shè)置keepalive,客戶端的請(qǐng)求會(huì)一直存在,直到客戶端下次發(fā)送信息時(shí)才能發(fā)現(xiàn)服務(wù)端已經(jīng)無法響應(yīng),會(huì)關(guān)閉連接。

??以上內(nèi)容都沒有經(jīng)過編碼驗(yàn)證,也沒有通過源碼確認(rèn),純粹百度和google搜索各種資料匯總。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容