TCP套接字端口復用SO_REUSEADDR


下面建立的套接字都是tcp套接字


1.進程創建監聽套接字socket1,邦定一個指定端口,並接受了若干連接。那么進程創建另外一個套接口socket2,並試圖邦定同一個端口時候,bind錯誤返回“Address already in use”(即使使用了SO_REUSEADDR).

2.進程創建監聽套接字,邦定一個指定端口,並接受了若干連接,為每個連接創建子進程為連接服務。殺死監聽套接字所在進程,然后重新啟動。重新啟動的進程調用bind重新建立監聽套接字。這次邦定只有在bind前指定了SO_REUSEADDR時才能成功。(因為直接殺進程,沒有顯式關閉套接字來釋放端口,會等待一段時間后才可以重新use這個關口,解決辦法就是用SO_REUSEADDR)。

3.進程創建套接字socket1,邦定一個指定端口,使用這個套接字去connect另外一個監聽套接字。連接建立。然后進程建立一個監聽套接字socket2,邦定同一個端口。這次邦定只有在下面兩個條件都滿足的情況下才成功返回:為socket2邦定前指定SO_REUSEADDR,且為socket1邦定前也指定了SO_REUSEADDR。

4.進程創建套接字socket1,邦定一個指定端口,去連接某個監聽套接口。殺死進程,保證socket1一端執行主動關閉。那么重啟進程后,除非上一個連接中socket1退出了TIME_WAIT狀態,否則重啟的進程在調用bind時候錯誤返回。

 

同一個機器上一個端口PORT1,TCP socket1 綁定PORT1,然后TCP socket2綁定PORT1會失敗;

同一個機器上一個端口PORT1,TCP socket1 綁定PORT1,然后UDP socket2綁定PORT1會成功;

同一個機器上一個端口PORT1,UDP socket1 綁定PORT1,然后UDP socket2綁定PORT1會失敗;

 

同一個機器上一個端口PORT1,TCP socket1 綁定PORT1,然后TCP socket2綁定PORT1會成功的條件是:

兩個套接字綁定前都調用:

int opt = 1;  

// sockfd為需要端口復用的套接字  

setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const voidvoid *)&opt, sizeof(opt));

但是假如socket1不僅bind了,還listen,並且accept成功了,這個時候socket2再次綁定到這個端口就會失敗!!但是假如socket2是UDP的socket,那么socket2的bind還是會成功的!!!

 

端口復用允許在一個應用程序可以把 n 個套接字綁在一個端口上而不出錯。同時,這 n 個套接字發送信息都正常,沒有問題。但是,這些套接字並不是所有都能讀取信息,只有最后一個套接字會正常接收數據。

端口復用最常用的用途應該是防止服務器重啟時之前綁定的端口還未釋放或者程序突然退出而系統沒有釋放端口。這種情況下如果設定了端口復用,則新啟動的服務器進程可以直接綁定端口。如果沒有設定端口復用,綁定會失敗,提示ADDR已經在使用中——那只好等等再重試了,麻煩!

 

復用真正是什么意義呢?這個我們可以看看TCP/IP里面TCP建立和斷開鏈接的方法。

我們知道,在TCP斷開鏈接的時候我們需要四次握手來斷開,而且當兩端都關閉了read/write通道以后我們還是要等待一個TIME_WAIT時間。

這就是SO_REUSEADDR的作用所在.

其實這個選項就是告訴OS如果一個端口處於TIME_WAIT狀態, 那么我們就不用等待直接進入使用模式, 不需要繼續等待這個時間結束.

那這樣我們肯定要問,那為什么我們需要有這個TIME_WAIT時間啊?

看看TCP/IP協議組我們就知道,這樣做是為了讓在網絡中殘余的TCP包消失, 也就是說, 如果我們沒有等到這個時間就讓OS把這個端口釋放給其他的進程使用,別的進程很有可能就會收到上一個會話的殘余TCP包,這樣就會出現一系列的不可預知的錯誤.

一、保證TCP協議的全雙工連接能夠可靠關閉
二、保證這次連接的重復數據段從網絡中消失

那么什么時候我們可以用這個選項以加快我們進程的速度減小等待時間呢?

這里有一些例子:

SO_REUSEADDR可以用在以下四種情況下。
(摘自《Unix網絡編程》卷一,即UNPv1)
1、當有一個有相同本地地址和端口的socket1處於TIME_WAIT狀態時,而你啟
動的程序的socket2要占用該地址和端口,你的程序就要用到該選項。
2、SO_REUSEADDR允許同一port上啟動同一服務器的多個實例(多個進程)。但
每個實例綁定的IP地址是不能相同的。在有多塊網卡或用IP Alias技術的機器可
以測試這種情況。
3、SO_REUSEADDR允許單個進程綁定相同的端口到多個socket上,但每個soc
ket綁定的ip地址不同。這和2很相似,區別請看UNPv1。
4、SO_REUSEADDR允許完全相同的地址和端口的重復綁定。但這只用於UDP的
多播,不用於TCP。

也就是說,不是所有的情況我們都可以使用這個選項的,請參閱這篇淘寶的案例:

http://rdc.taobao.com/blog/cs/?p=1195

 

1、一般來說,一個端口釋放后會等待兩分鍾之后才能再被使用,SO_REUSEADDR是讓端口釋放后立即就可以被再次使用。

    SO_REUSEADDR用於對TCP套接字處於TIME_WAIT狀態下的socket,才可以重復綁定使用。server程序總是應該在調用bind()之前設置SO_REUSEADDR套接字選項。TCP,先調用close()的一方會進入TIME_WAIT狀態

2、SO_REUSEADDR和SO_REUSEPORT

SO_REUSEADDR提供如下四個功能:

    SO_REUSEADDR允許啟動一個監聽服務器並捆綁其眾所周知端口,即使以前建立的將此端口用做他們的本地端口的連接仍存在。這通常是重啟監聽服務器時出現,若不設置此選項,則bind時將出錯。

    SO_REUSEADDR允許在同一端口上啟動同一服務器的多個實例,只要每個實例捆綁一個不同的本地IP地址即可。對於TCP,我們根本不可能啟動捆綁相同IP地址和相同端口號的多個服務器。

    SO_REUSEADDR允許單個進程捆綁同一端口到多個套接口上,只要每個捆綁指定不同的本地IP地址即可。這一般不用於TCP服務器。

    SO_REUSEADDR允許完全重復的捆綁:當一個IP地址和端口綁定到某個套接口上時,還允許此IP地址和端口捆綁到另一個套接口上。一般來說,這個特性僅在支持多播的系統上才有,而且只對UDP套接口而言(TCP不支持多播)。

SO_REUSEPORT選項有如下語義:

    此選項允許完全重復捆綁,但僅在想捆綁相同IP地址和端口的套接口都指定了此套接口選項才行。

    如果被捆綁的IP地址是一個多播地址,則SO_REUSEADDR和SO_REUSEPORT等效。

使用這兩個套接口選項的建議:

    在所有TCP服務器中,在調用bind之前設置SO_REUSEADDR套接口選項;

當編寫一個同一時刻在同一主機上可運行多次的多播應用程序時,設置SO_REUSEADDR選項,並將本組的多播地址作為本地IP地址捆綁。

    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,

   (const void *)&nOptval , sizeof(int)) < 0) ...

    Q:編寫 TCP/SOCK_STREAM 服務程序時,SO_REUSEADDR到底什么意思?

    A:這個套接字選項通知內核,如果端口忙,但TCP狀態位於 TIME_WAIT ,可以重用端口。如果端口忙,而TCP狀態位於其他狀態,重用端口時依舊得到一個錯誤信息,指明"地址已經使用中"。如果你的服務程序停止后想立即重啟,而新套接字依舊使用同一端口,此時SO_REUSEADDR 選項非常有用。必須意識到,此時任何非期望數據到達,都可能導致服務程序反應混亂,不過這只是一種可能,事實上很不可能。

    一個套接字由相關五元組構成,協議、本地地址、本地端口、遠程地址、遠程端口。SO_REUSEADDR 僅僅表示可以重用本地本地地址、本地端口,整個相關五元組還是唯一確定的。所以,重啟后的服務程序有可能收到非期望數據。必須慎重使用SO_REUSEADDR 選項。【2】

 


免責聲明!

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



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