天天看點

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預設操作:立即傳回

Linux網絡程式設計socket選項之SO_LINGER,SO_REUSEADDR

此種情況,close立即傳回,如果send buffer中還有資料,close将會等到所有資料被發送完之後之後傳回。由于我們并沒有等待對方tcp發送的ack資訊,是以我們隻能保證資料已經發送到對方,我們并不知道對方是否已經接受了資料。由于此種情況,tcp連接配接終止是按照正常的4次握手方式,需要經過time_wait。

   b.    l_onoff非0,并且使之l_linger為一個整數

Linux網絡程式設計socket選項之SO_LINGER,SO_REUSEADDR

在這種情況下,close會在接收到對方tcp的ack資訊之後才傳回(l_linger消耗完之前)。但是這種ack資訊隻能保證對方已經接收到資料,并不保證對方應用程式已經讀取資料。

c.    l_linger設定值太小

Linux網絡程式設計socket選項之SO_LINGER,SO_REUSEADDR

這種情況,由于l_linger值太小,在send buffer中的資料都發送完之前,close就傳回,此種情況終止tcp連接配接,更l_linger = 0類似,tcp連接配接終止不是按照正常的4步握手,是以,tcp連接配接不會進入time_wait狀态,那麼,client會向server發送一個rst資訊.

d.    shutdown,等待應用程式讀取資料

Linux網絡程式設計socket選項之SO_LINGER,SO_REUSEADDR

同上面的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到一個端口,既是這個端口已經被一個正在運作的連接配接使用了.

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

一個監聽(listen)server已經啟動

當有client有連接配接請求的時候,server産生一個子程序去處理該client的事物.

server主程序終止了,但是子程序還在占用該連接配接處理client的事情.雖然子程序終止了,但是由于子程序沒有終止,該socket的引用計數不會為0,是以該socket不會被關閉.

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中,是允許的.

版權申明:

轉載文章請注明原文出處http://blog.csdn.net/feiyinzilgd/archive/2010/09/19/5894300.aspx

并請聯系譚海燕本人或者前往譚海燕個人首頁留言

繼續閱讀