TCP連接關閉總結


由於涉及面太廣,只作簡單整理,有興趣的可參考《UNIX Networking Programming》volum 1, Section 5.7, 5.12, 5.14, 5.15, 6.6 以及7.5 SO_LINGER選項。

以一個簡單的echo服務器為例,客戶端從標准輸入讀入字符,發送給服務器,服務器收到后再原樣返回,客戶端收到后打印到標准輸出。

那么,關於套接字的關閉有以下幾種情形:

1,客戶端關閉連接:

1.1,客戶端調用close()

1.2,客戶端進程關閉

1.3,客戶端調用shutdown()

1.4,客戶端調用close()+SO_LINGER選項

1.5,客戶端崩潰

 

2,服務器關閉連接:

2.1,服務器調用close()

2.2,服務器進程關閉

2.3,服務器崩潰

2.4,服務器崩潰+SO_KEEPALIVE選項

============================分割線===============================

1.1與1.2等價,就算客戶端進程關閉,系統內核也會自動close(socket),且注意,當socket引用為0時才會真正調用close(),close()總是立即返回的,然后由系統嘗試發送完內核緩沖區內的所有數據,接着才發送FIN。

說道這里,不得不談談TCP連接關閉的四次握手。可以看成是2組FIN, ACK。主動關閉的一方先發送FIN,收到ACK后,進入FIN_WAIT2狀態,此時也叫做“半關閉”狀態,特別須要注意的是,此時客戶端套接字依然可以接收數據包,但是不能發送數據包。 被動關閉的一方,此時收到FIN了,一般情況下都是由於read(socket)返回0,然后得知對方關閉,close(socket)后,另外一組FIN,ACK隨之產生,此時主動方進入TIME_WAIT狀態。即四次握手完成。

以上即是正常情況下連接關閉的情形。

再看看1.3,shutdown()與close()主要有3點區別:

shutdown()不理會引用計數與內核緩沖區內剩余待發數據包,直接發送FIN;

shutdown()可以只關閉套接字某個方向的連接,例如關閉發送,關閉接收,或者2者都關閉;

實際上shutdown(write)后,就是上面說的半關閉情形,依然可以完成四次握手。

再看看1.4,為什么要設置SO_LINGER呢

SO_LINGER的目的就是改變close()的默認行為,可以決定close()在哪個狀態返回,或者讓套接字立即發送RST,從而沒有FIN的發送。接收方返回ECONNRESET錯誤,連接直接關閉。

再來總結下1.1-1.4,這么多關閉連接的方式,那么什么方式才是最好的呢?

擇優選擇的方式當然是考慮最惡劣的情況,對方主機崩潰或網絡故障導致數據包傳輸停滯。

RST不用考慮了,直接TIME_WAIT狀態都沒,如果有網絡故障,可能下次創建的套接字還會接收到已經被銷毀的套接字的數據報。

close()不能保證對方一定收到FIN。

close()+SO_LINGER雖然能控制close()在收到ACK后返回,依然不能保證四次握手完成。

shutdown()先進入半關閉狀態,再調用read(),返回0(收到對方FIN)則說明四次握手正常進行,此為最優方式。

其實仔細想想,一般情況也不用這么麻煩,拿網游服務器來說,客戶端close()后,就算服務器不知道,那么這種情況歸為1.5討論;如果是服務端close()而客戶端不知道,那么歸為2.3討論。總之都有解決辦法。。

現在再討論1.5,很簡單,服務端加入鏈路異常檢測機制即可,這也是所有大型TCP服務器必備的機制,定時發送小數據包檢測客戶端是否有異常退出。

=============================分割線==============================

服務器關閉連接方面:

2.1,2.2等價,一般情況下也與1.1,1.2等價,只是主動關閉方是服務器了。

但是,在我們討論的例子里,客戶端要從標准輸入讀字符,這是阻塞方式,服務端關閉連接后,客戶端無法知道,因為它阻塞在標准輸入了,當我們再次輸入字符,並發送,收到FIN或RST,此時客戶端才關閉。總之,客戶端由於某種原因,不能及時調用read(),所以無法得知服務器關閉了連接。

2.3,服務器崩潰,客戶端由於一直收不到ACK,會一直嘗試發送數據,標准socket大概是9分鍾后才會返回錯誤。

2.3,服務器崩潰,客戶端又長時間與服務器沒有數據交互,此時設置SO_KEEPALIVE選項可得知。

=============================分割線=============================

后記:網絡是門復雜的學問,由此TCP連接的關閉可見一斑。普通程序員通常不會考慮這么細致,但是我相信這些問題一直困擾着他們。

 

補充說明:經試驗,在Windows平台,1.2  2.2情況等同於close()+SO_LINGER選項直接發送RST,可能由於系統必須及時清理資源吧,這點與linux是不同的,有興趣的可以試試。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM