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會成功;socket

同一個機器上一個端口PORT1,UDP socket1 綁定PORT1,而後UDP socket2綁定PORT1會失敗;tcp

 

同一個機器上一個端口PORT1,TCP socket1 綁定PORT1,而後TCP socket2綁定PORT1會成功的條件是:測試

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

int opt = 1;  server

// sockfd爲須要端口複用的套接字  blog

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包,這樣就會出現一系列的不可預知的錯誤.

1、保證TCP協議的全雙工鏈接可以可靠關閉
2、保證此次鏈接的重複數據段從網絡中消失

那麼何時咱們能夠用這個選項以加快咱們進程的速度減少等待時間呢?

這裏有一些例子:

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

也就是說,不是全部的狀況咱們均可以使用這個選項的,請參閱這篇淘寶的案例:

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

 

一、通常來講,一個端口釋放後會等待兩分鐘以後才能再被使用,SO_REUSEADDR是讓端口釋放後當即就能夠被再次使用。

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

二、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】

相關文章
相關標籤/搜索