閱讀466 返回首頁    go 技術社區[雲棲]


Linux網絡編程socket選項之SO_LINGER,SO_REUSEADDR

                       Linux網絡編程socket選項之SO_LINGER,SO_REUSEADDR

Linux網絡編程中,socket的選項很多.其中幾個比較重要的選項有:SO_LINGER(僅僅適用於TCP,SCTP), SO_REUSEADDR.

 

  • SO_LINGER

在默認情況下,當調用close關閉socke的使用,close會立即返回,但是,如果send buffer中還有數據,係統會試著先把send buffer中的數據發送出去,然後close才返回.

 

SO_LINGER選項則是用來修改這種默認操作的.於SO_LINGER相關聯的一個結構體如下:

#include <sys/socket.h> struct linger { int l_onoff //0=off, nonzero=on(開關) int l_linger //linger time(延遲時間) }

 

當調用setsockopt之後,該選項產生的影響取決於linger結構體中 l_onoff和l_linger的值:

0 = l_onoff

當l_onoff被設置為0的時候,將會關閉SO_LINGER選項,即TCP或則SCTP保持默認操作:close立即返回.l_linger值被忽略.

l_lineoff值非0,0 = l_linger

當調用close的時候,TCP連接會立即斷開.send buffer中未被發送的數據將被丟棄,並向對方發送一個RST信息.值得注意的是,由於這種方式,是非正常的4中握手方式結束TCP鏈接,所以,TCP連接將不會進入TIME_WAIT狀態,這樣會導致新建立的可能和就連接的數據造成混亂。具體原因詳見我的上一篇文章《linux 網絡編程之TIME_WAIT狀態》

 

  • l_onoff和l_linger都是非0

在這種情況下,回事的close返回得到延遲。調用close去關閉socket的時候,內核將會延遲。也就是說,如果send buffer中還有數據尚未發送,該進程將會被休眠直到一下任何一種情況發生:

 

1)    send buffer中的所有數據都被發送並且得到對方TCP的應答消息(這種應答並不是意味著對方應用程序已經接收到數據,在後麵shutdown將會具體講道)
2)    延遲時間消耗完。在延遲時間被消耗完之後,send buffer中的所有數據都將會被丟棄。

 

上麵1),2)兩種情況中,如果socket被設置為O_NONBLOCK狀態,程序將不會等待close返回,send buffer中的所有數據都將會被丟棄。所以,需要我們判斷close的返回值。在send buffer中的所有數據都被發送之前並且延遲時間沒有消耗完,close返回的話,close將會返回一個EWOULDBLOCK的error.

下麵用幾個實例來說明:


A.    Close默認操作:立即返回

                                         

此種情況,close立即返回,如果send buffer中還有數據,close將會等到所有數據被發送完之後之後返回。由於我們並沒有等待對方TCP發送的ACK信息,所以我們隻能保證數據已經發送到對方,我們並不知道對方是否已經接受了數據。由於此種情況,TCP連接終止是按照正常的4次握手方式,需要經過TIME_WAIT。

 

   B.    l_onoff非0,並且使之l_linger為一個整數

 

 

                                        

在這種情況下,close會在接收到對方TCP的ACK信息之後才返回(l_linger消耗完之前)。但是這種ACK信息隻能保證對方已經接收到數據,並不保證對方應用程序已經讀取數據。

 

 

C.    l_linger設置值太小

 

 

                                         

 

這種情況,由於l_linger值太小,在send buffer中的數據都發送完之前,close就返回,此種情況終止TCP連接,更l_linger = 0類似,TCP連接終止不是按照正常的4步握手,所以,TCP連接不會進入TIME_WAIT狀態,那麼,client會向server發送一個RST信息.

 

 

D.    Shutdown,等待應用程序讀取數據

 

 

 

                                        

同上麵的B進行對比,調用shutdown後緊接著調用read,此時read會被阻塞,直到接收到對方的FIN,也就是說read是在server的應用程序調用close之後才返回的。當server應用程序讀取到來自client的數據和FIN之後,server會進入一個叫CLOSE_WAIT,關於CLOSE_WAIT,詳見我的博客《 Linux 網絡編程 之 TCP狀態轉換》 。那麼,如果server端要斷開該TCP連接,需要server應用程序調用一次close,也就意味著向client發送FIN。這個時候,說明server端的應用程序已經讀取到client發送的數據和FIN。read會在接收到server的FIN之後返回。所以,shutdown 可以確保server端應用程序已經讀取數據了,而不僅僅是server已經接收到數據而已。

 

shutdown參數如下:

SHUT_RD:調用shutdown的一端receive buffer將被丟棄掉,無法接受數據,但是可以發送數據,send buffer的數據可以被發送出去

SHUT_WR:調用shutdown的一端無法發送數據,但是可以接受數據.該參數表示不能調用send.但是如果還有數據在send buffer中,這些數據還是會被繼續發送出去的.

 

 

 

  • SO_REUSEADDR和SO_REUSEPORT

最近,看到CSDN的linux版塊,有人提問,說為什麼server程序重啟之後,無法連接,需要過一段時間才能連接上.我想對於這個問題,有兩種可能:一種可能就是該server一直停留在TIME_WAIT狀態.這個時候,需要等待2MSL的時間才能重新連接上,具體細節原因請見我的另一篇文章《linux 網絡編程之TIME_WAIT狀態》

另一種可能就是SO_REUSEADDR參數設置問題.關於TIME_WAIT的我就不在這裏重述了,這裏我講一講SO_REUSEADDR.

 

  • SO_REUSEADDR允許一個server程序listen監聽並bind到一個端口,既是這個端口已經被一個正在運行的連接使用了.

我們一般會在下麵這種情況中遇到:

 

  1. 一個監聽(listen)server已經啟動
  2. 當有client有連接請求的時候,server產生一個子進程去處理該client的事物.
  3. server主進程終止了,但是子進程還在占用該連接處理client的事情.雖然子進程終止了,但是由於子進程沒有終止,該socket的引用計數不會為0,所以該socket不會被關閉.
  4. server程序重啟.

 

默認情況下,server重啟,調用socket,bind,然後listen,會失敗.因為該端口正在被使用.如果設定SO_REUSEADDR,那麼server重啟才會成功.因此,所有的TCP server都必須設定此選項,用以應對server重啟的現象.

 

 

  • SO_REUSEADDR允許同一個端口上綁定多個IP.隻要這些IP不同.另外,還可以在綁定IP通配符.但是最好是先綁定確定的IP,最後綁定通配符IP.一麵係統拒絕.簡而言之,SO_REUSEADDR允許多個server綁定到同一個port上,隻要這些server指定的IP不同,但是SO_REUSEADDR需要在bind調用之前就設定.在TCP中,不允許建立起一個已經存在的相同的IP和端口的連接.但是在UDP中,是允許的.

 


版權申明:
轉載文章請注明原文出處https://blog.csdn.net/feiyinzilgd/archive/2010/09/19/5894300.aspx
並請聯係譚海燕本人或者前往譚海燕個人主頁留言

最後更新:2017-04-02 06:51:25

  上一篇:go C/C++ 宏帶來的奇技淫巧
  下一篇:go magento -- 對版本升級帶來的語言包重新修正發點牢騷