這篇文章給大家介紹SO_REUSEPORT與SO_REUSEADDR的區(qū)別有哪些,內(nèi)容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
創(chuàng)新互聯(lián)專注于北海網(wǎng)站建設服務及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供北海營銷型網(wǎng)站建設,北海網(wǎng)站制作、北海網(wǎng)頁設計、北海網(wǎng)站官網(wǎng)定制、成都小程序開發(fā)服務,打造北海網(wǎng)絡公司原創(chuàng)品牌,更為您提供北海網(wǎng)站排名全網(wǎng)營銷落地服務。Socket的基本背景
在討論這兩個選項的區(qū)別時,我們需要知道的是BSD實現(xiàn)是所有socket實現(xiàn)的起源?;旧掀渌械南到y(tǒng)某種程度上都參考了BSD socket實現(xiàn)(或者至少是其接口),然后開始了它們自己的獨立發(fā)展進化。顯然,BSD本身也是隨著時間在不斷發(fā)展變化的。所以較晚參考BSD的系統(tǒng)比較早參考BSD的系統(tǒng)多一些特性。所以理解BSD socket實現(xiàn)是理解其他socket實現(xiàn)的基石。下面我們就分析一下BSD socket實現(xiàn)。
在這之前,我們首先要明白如何唯一識別TCP/UDP連接。TCP/UDP是由以下五元組唯一地識別的:
{<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}
這些數(shù)值組成的任何獨特的組合可以唯一地確一個連接。那么,對于任意連接,這五個值都不能完全相同。否則的話操作系統(tǒng)就無法區(qū)別這些連接了。
一個socket的協(xié)議是在用socket()初始化的時候就設置好的。源地址(source address)和源端口(source port)在調(diào)用bind()的時候設置。目的地址(destination address)和目的端口(destination port)在調(diào)用connect()的時候設置。其中UDP是無連接的,UDP socket可以在未與目的端口連接的情況下使用。但UDP也可以在某些情況下先與目的地址和端口建立連接后使用。在使用無連接UDP發(fā)送數(shù)據(jù)的情況下,如果沒有顯式地調(diào)用bind(),草錯系統(tǒng)會在第一次發(fā)送數(shù)據(jù)時自動將UDP socket與本機的地址和某個端口綁定(否則的話程序無法接受任何遠程主機回復的數(shù)據(jù))。同樣的,一個沒有綁定地址的TCP socket也會在建立連接時被自動綁定一個本機地址和端口。
如果我們手動綁定一個端口,我們可以將socket綁定至端口0,綁定至端口0的意思是讓系統(tǒng)自己決定使用哪個端口(一般是從一組操作系統(tǒng)特定的提前決定的端口數(shù)范圍中),所以也就是任何端口的意思。同樣的,我們也可以使用一個通配符來讓系統(tǒng)決定綁定哪個源地址(ipv4通配符為0.0.0.0,ipv6通配符為::)。而與端口不同的是,一個socket可以被綁定到主機上所有接口所對應的地址中的任意一個。基于連接在本socket的目的地址和路由表中對應的信息,操作系統(tǒng)將會選擇合適的地址來綁定這個socket,并用這個地址來取代之前的通配符IP地址。
在默認情況下,任意兩個socket不能被綁定在同一個源地址和源端口組合上。比如說我們將socketA綁定在A:X地址,將socketB綁定在B:Y地址,其中A和B是IP地址,X和Y是端口。那么在A==B的情況下X!=Y必須滿足,在X==Y的情況下A!=B必須滿足。需要注意的是,如果某一個socket被綁定在通配符IP地址下,那么事實上本機所有IP都會被系統(tǒng)認為與其綁定了。例如一個socket綁定了0.0.0.0:21,在這種情況下,任何其他socket不論選擇哪一個具體的IP地址,其都不能再綁定在21端口下。因為通配符IP0.0.0.0與所有本地IP都沖突。
以上所有內(nèi)容基本上在主要操作系統(tǒng)中都相同。而各個中SO_REUSEADDR會有不同的含義。首先我們來討論BSD實現(xiàn)。因為BSD試試其他所有socket實現(xiàn)方法的源頭。
BSD
SO_REUSEADDR
如果在一個socket綁定到某一地址和端口之前設置了其SO_REUSEADDR的屬性,那么除非本socket與產(chǎn)生了嘗試與另一個socket綁定到完全相同的源地址和源端口組合的沖突,否則的話這個socket就可以成功的綁定這個地址端口對。這聽起來似乎和之前一樣。但是其中的關(guān)鍵字是完全。SO_REUSEADDR主要改變了系統(tǒng)對待通配符IP地址沖突的方式。
如果不用SO_REUSEADDR的話,如果我們將socketA綁定到0.0.0.0:21,那么任何將本機其他socket綁定到端口21的舉動(如綁定到192.168.1.1:21)都會導致EADDRINUSE錯誤。因為0.0.0.0是一個通配符IP地址,意味著任意一個IP地址,所以任何其他本機上的IP地址都被系統(tǒng)認為已被占用。如果設置了SO_REUSEADDR選項,因為0.0.0.0:21和192.168.1.1:21并不是完全相同的地址端口對(其中一個是通配符IP地址,另一個是一個本機的具體IP地址),所以這樣的綁定是可以成功的。需要注意的是,無論socketA和socketB初始化的順序如何,只要設置了SO_REUSEADDR,綁定都會成功;而只要沒有設置SO_REUSEADDR,綁定都不會成功。
下面的表格列出了一些可能的情況及其結(jié)果。
SO_REUSEADDR | socketA | socketB | Result |
---|---|---|---|
ON / OFF | 192.168.1.1:21 | 192.168.1.1:21 | ERROR(EADDRINUSE) |
ON / OFF | 192.168.1.1:21 | 10.0.1.1:21 | OK |
ON / OFF | 10.0.1.1:21 | 192.168.1.1:21 | OK |
OFF | 192.168.1.1:21 | 0.0.0.0:21 | ERROR(EADDRINUSE) |
OFF | 0.0.0.0:21 | 192.168.1.1:21 | ERROR(EADDRINUSE) |
ON | 192.168.1.1:21 | 0.0.0.0:21 | OK |
ON | 0.0.0.0:21 | 192.168.1.1:21 | OK |
ON / OFF | 0.0.0.0:21 | 0.0.0.0:21 | OK |
這個表格假定socketA已經(jīng)成功地綁定了表格中對應的地址,然后socketB被初始化了,其SO_REUSEADDR設置的情況如表格第一列所示,然后socketB試圖綁定表格中對應地址。Result列是其綁定的結(jié)果。如果第一列中的值是ON/OFF,那么SO_REUSEADDR設置與否都與結(jié)果無關(guān)。
上面討論了SO_REUSEADDR對通配符IP地址的作用,但其并不只有這一作用。其另一作用也是為什么大家在進行服務器端編程的時候會采用SO_REUSEADDR選項的原因。為了理解其另一個作用及其重要應用,我們需要先更深入地討論一下TCP協(xié)議的工作原理。
每一個socket都有其相應的發(fā)送緩沖區(qū)(buffer)。當成功調(diào)用其send()方法的時候,實際上我們所要求發(fā)送的數(shù)據(jù)并不一定被立即發(fā)送出去,而是被添加到了發(fā)送緩沖區(qū)中。對于UDP socket來說,即使不是馬上被發(fā)送,這些數(shù)據(jù)一般也會被很快發(fā)送出去。但對于TCP socket來說,在將數(shù)據(jù)添加到發(fā)送緩沖區(qū)之后,可能需要等待相對較長的時間之后數(shù)據(jù)才會被真正發(fā)送出去。因此,當我們關(guān)閉了一個TCP socket之后,其發(fā)送緩沖區(qū)中可能實際上還仍然有等待發(fā)送的數(shù)據(jù)。但此時因為send()返回了成功,我們的代碼認為數(shù)據(jù)已經(jīng)實際上被成功發(fā)送了。如果TCP socket在我們調(diào)用close()之后直接關(guān)閉,那么所有這些數(shù)據(jù)都將會丟失,而我們的代碼根本不會知道。但是,TCP是一個可靠的傳輸層協(xié)議,直接丟棄這些待傳輸?shù)臄?shù)據(jù)顯然是不可取的。實際上,如果在socket的發(fā)送緩沖區(qū)中還有待發(fā)送數(shù)據(jù)的情況下調(diào)用了其close()方法,其將會進入一個所謂的TIME_WAIT狀態(tài)。在這個狀態(tài)下,socket將會持續(xù)嘗試發(fā)送緩沖區(qū)的數(shù)據(jù)直到所有數(shù)據(jù)都被成功發(fā)送或者直到超時,超時被觸發(fā)的情況下socket將會被強制關(guān)閉。
操作系統(tǒng)的kernel在強制關(guān)閉一個socket之前的最長等待時間被稱為延遲時間(Linger Time)。在大部分系統(tǒng)中延遲時間都已經(jīng)被全局設置好了,并且相對較長(大部分系統(tǒng)將其設置為2分鐘)。我們也可以在初始化一個socket的時候使用SO_LINGER選項來特定地設置每一個socket的延遲時間。我們甚至可以完全關(guān)閉延遲等待。但是需要注意的是,將延遲時間設置為0(完全關(guān)閉延遲等待)并不是一個好的編程實踐。因為優(yōu)雅地關(guān)閉TCP socket是一個比較復雜的過程,過程中包括與遠程主機交換數(shù)個數(shù)據(jù)包(包括在丟包的情況下的丟失重傳),而這個數(shù)據(jù)包交換的過程所需要的時間也包括在延遲時間中。如果我們停用延遲等待,socket不止會在關(guān)閉的時候直接丟棄所有待發(fā)送的數(shù)據(jù),而且總是會被強制關(guān)閉(由于TCP是面向連接的協(xié)議,不與遠端端口交換關(guān)閉數(shù)據(jù)包將會導致遠端端口處于長時間的等待狀態(tài))。所以通常我們并不推薦在實際編程中這樣做。TCP斷開連接的過程超出了本文討論的范圍,如果對此有興趣,可以參考這個頁面。并且實際上,如果我們禁用了延遲等待,而我們的程序沒有顯式地關(guān)閉socket就退出了,BSD(可能包括其他系統(tǒng))會忽略我們的設置進行延遲等待。例如,如果我們的程序調(diào)用了exit()方法,或者其進程被使用某個信號終止了(包括進程因為非法內(nèi)存訪問之類的情況而崩潰)。所以我們無法百分之百保證一個socket在所有情況下忽略延遲等待時間而終止。
這里的問題在于操作系統(tǒng)如何對待處于TIME_WAIT階段的socket。如果SO_REUSEADDR選項沒有被設置,處于TIME_WAIT階段的socket任然被認為是綁定在原來那個地址和端口上的。直到該socket被完全關(guān)閉之前(結(jié)束TIME_WAIT階段),任何其他企圖將一個新socket綁定該該地址端口對的操作都無法成功。這一等待的過程可能和延遲等待的時間一樣長。所以我們并不能馬上將一個新的socket綁定到一個剛剛被關(guān)閉的socket對應的地址端口對上。在大多數(shù)情況下這種操作都會失敗。
然而,如果我們在新的socket上設置了SO_REUSEADDR選項,如果此時有另一個socket綁定在當前的地址端口對且處于TIME_WAIT階段,那么這個已存在的綁定關(guān)系將會被忽略。事實上處于TIME_WAIT階段的socket已經(jīng)是半關(guān)閉的狀態(tài),將一個新的socket綁定在這個地址端口對上不會有任何問題。這樣的話原來綁定在這個端口上的socket一般不會對新的socket產(chǎn)生影響。但需要注意的是,在某些時候,將一個新的socket綁定在一個處于TIME_WAIT階段但仍在工作的socket所對應的地址端口對會產(chǎn)生一些我們并不想要的,無法預料的負面影響。但這個問題超過了本文的討論范圍。而且幸運的是這些負面影響在實踐中很少見到。
最后,關(guān)于SO_REUSEADDR,我們還要注意的一件事是,以上所有內(nèi)容只要我們對新的socket設置了SO_REUSEADDR就成立。至于原有的已經(jīng)綁定在當前地址端口對上的,處于或不處于TIME_WAIT階段的socket是否設置了SO_REUSEADDR并無影響。決定bind操作是否成功的代碼僅僅會檢查新的被傳遞到bind()方法的socket的SO_REUSEADDR選項。其他涉及到的socket的SO_REUSEADDR選項并不會被檢查。
SO_REUSEPORT
許多人將SO_REUSEADDR當成了SO_REUSEPORT。基本上來說,SO_REUSEPORT允許我們將任意數(shù)目的socket綁定到完全相同的源地址端口對上,只要所有之前綁定的socket都設置了SO_REUSEPORT選項。如果第一個綁定在該地址端口對上的socket沒有設置SO_REUSEPORT,無論之后的socket是否設置SO_REUSEPORT,其都無法綁定在與這個地址端口完全相同的地址上。除非第一個綁定在這個地址端口對上的socket釋放了這個綁定關(guān)系。與SO_REUSEADDR不同的是 ,處理SO_REUSEPORT的代碼不僅會檢查當前嘗試綁定的socket的SO_REUSEPORT,而且也會檢查之前已綁定了當前嘗試綁定的地址端口對的socket的SO_REUSEPORT選項。
SO_REUSEPORT并不等于SO_REUSEADDR。這么說的含義是如果一個已經(jīng)綁定了地址的socket沒有設置SO_REUSEPORT,而另一個新socket設置了SO_REUSEPORT且嘗試綁定到與當前socket完全相同的端口地址對,這次綁定嘗試將會失敗。同時,如果當前socket已經(jīng)處于TIME_WAIT階段,而這個設置了SO_REUSEPORT選項的新socket嘗試綁定到當前地址,這個綁定操作也會失敗。為了能夠?qū)⑿碌膕ocket綁定到一個當前處于TIME_WAIT階段的socket對應的地址端口對上,我們要么需要在綁定之前設置這個新socket的SO_REUSEADDR選項,要么需要在綁定之前給兩個socket都設置SO_REUSEPORT選項。當然,同時給socket設置SO_REUSEADDR和SO_REUSEPORT選項是也是可以的。
SO_REUSEPORT是在SO_REUSEADDR之后被添加到BSD系統(tǒng)中的。這也是為什么現(xiàn)在有些系統(tǒng)的socket實現(xiàn)里沒有SO_REUSEPORT選項。因為它們在這個選項被加入BSD系統(tǒng)之前參考了BSD的socket實現(xiàn)。而在這個選項被加入之前,BSD系統(tǒng)下沒有任何辦法能夠?qū)蓚€socket綁定在完全相同的地址端口對上。
Connect()返回EADDRINUSE?
有些時候bind()操作會返回EADDRINUSE錯誤。但奇怪的是,在我們調(diào)用connect()操作時,也有可能得到EADDRINUSE錯誤。這是為什么呢?為何一個我們嘗試令當前端口建立連接的遠程地址也會被占用呢?難道將多個socket連接到同一個遠程地址的操作會有什么問題產(chǎn)生嗎?
正如本文之前所說,一個連接關(guān)系是由一個五元組確定的。對于任意的連接關(guān)系而言,這個五元組必須是唯一的。否則的話,系統(tǒng)將無法分辨兩個連接。而現(xiàn)在當我們采用了地址復用之后,我們可以將兩個采用相同協(xié)議的socket綁定到同一地址端口對上。這意味著對這兩個socket而言,五元組里的{<protocol>, <src addr>, <src port>}已經(jīng)相同了。在這種情況下,如果我們嘗試將它們都連接到同一個遠程地址端口上,這兩個連接關(guān)系的五元組將完全相同。也就是說,產(chǎn)生了兩個完全相同的連接。在TCP協(xié)議中這是不被允許的(UDP是無連接的)。如果這兩個完全相同的連接種的某一個接收到了數(shù)據(jù),系統(tǒng)將無法分辨這個數(shù)據(jù)到底屬于哪個連接。所以在這種情況下,至少這兩個socket所嘗試連接的遠程主機的地址和端口不能相同。只有如此,系統(tǒng)才能繼續(xù)區(qū)分這兩個連接關(guān)系。
所以當我們將兩個采用相同協(xié)議的socket綁定到同一個本地地址端口對上后,如果我們還嘗試讓它們和同一個目的地址端口對建立連接,第二個嘗試調(diào)用connect()方法的socket將會報EADDRINUSE的錯誤,這說明一個擁有完全相同的五元組的socket已經(jīng)存在了。
Multicast Address
相對于用于一對一通信的unicast地址,multicast地址用于一對多通信。IPv4和IPv6都擁有multicast地址。但是IPv4中的multicast實際上在公共網(wǎng)路上很少被使用。
SO_REUSEADDR的意義在multicast地址的情況下會與之前有所不同。在這種情況下,SO_REUSEADDR允許我們將多個socket綁定至完全相同的源廣播地址端口對上。換句話說,對于multicast地址而言,SO_REUSEADDR的作用相當于unicast通信中的SO_REUSEPORT。事實上,在multicast情況下,SO_REUSEADDR和SO_REUSEPORT的作用完全相同。
FreeBSD/OpenBSD/NetBSD
所有這些系統(tǒng)都是參考了較新的原生BSD系統(tǒng)代碼。所以這三個系統(tǒng)提供與BSD完全相同的socket選項,這些選項的含義與原生BSD完全相同。
MacOS X
MacOS X的核心代碼實現(xiàn)是基于較新版本的原生BSD的BSD風格的UNIX,所以MacOS X提供與BSD完全相同的socket選項,并且它們的含義也與BSD系統(tǒng)相同。
iOS
iOS事實上是一個略微改造過的MacOS X,所以適用于MacOS X的也適用于iOS。
Linux
在Linux3.9之前,只有SO_REUSEADDR選項存在。這個選項的作用基本上同BSD系統(tǒng)下相同。但其仍有兩個重要的區(qū)別。
第一個區(qū)別是如果一個處于監(jiān)聽(服務器)狀態(tài)下的TCP socket已經(jīng)被綁定到了一個通配符IP地址和一個特定端口下,那么不論這兩個socket有沒有設置SO_REUSEADDR選項,任何其他TCP socket都無法再被綁定到相同的端口下。即使另一個socket使用了一個具體IP地址(像在BSD系統(tǒng)中允許的那樣)也不行。而非監(jiān)聽(客戶)TCP socket則無此限制。
第二個區(qū)別是對于UDP socket來說,SO_REUSEADDR的作用和BSD中SO_REUSEPORT完全相同。所以兩個UDP socket如果都設置了SO_REUSEADDR的話,它們就可以被綁定在一組完全相同的地址端口對上。
Linux3.9加入了SO_REUSEPORT選項。只要所有socket(包括第一個)在綁定地址前設置了這個選項,兩個或多個,TCP或UDP,監(jiān)聽(服務器)或非監(jiān)聽(客戶)socket就可以被綁定在完全相同的地址端口組合下。同時,為了防止端口劫持(port hijacking),還有一個特別的限制:所有試圖綁定在相同的地址端口組合的socket必須屬于擁有相同用戶ID的進程。所以一個用戶無法從另一個用戶那里“偷竊”端口。
除此之外,對于設置了SO_REUSEPORT選項的socket,Linux kernel還會執(zhí)行一些別的系統(tǒng)所沒有的特別的操作:對于綁定于同一地址端口組合上的UDP socket,kernel嘗試在它們之間平均分配收到的數(shù)據(jù)包;對于綁定于同一地址端口組合上的TCP監(jiān)聽socket,kernel嘗試在它們之間平均分配收到的連接請求(調(diào)用accept()方法所得到的請求)。這意味著相比于其他允許地址復用但隨機將收到的數(shù)據(jù)包或者連接請求分配給連接在同一地址端口組合上的socket的系統(tǒng)而言,Linux嘗試了進行流量分配上的優(yōu)化。比如一個簡單的服務器進程的幾個不同實例可以方便地使用SO_REUSEPORT來實現(xiàn)一個簡單的負載均衡,而且這個負載均衡有kernel負責, 對程序來說完全免費!
Android
Android的核心部分是略微修改過的Linux kernel,所以所有適用于Linux的操作也適用于Android。
Windows
Windows僅有SO_REUSEADDR選項。在Windows中對一個socket設置SO_REUSEADDR的效果與在BSD下同時對一個socket設置SO_REUSEPORT和SO_REUSEADDR相同。但其區(qū)別在于:即使另一個已綁定地址的socket并沒有設置SO_REUSEADDR,一個設置了SO_REUSEADDR的socket總是可以綁定到與另一個已綁定的socket完全相同的地址端口組合上。這個行為可以說是有些危險的。因為它允許了一個應用從另一個引用已連接的端口上偷取數(shù)據(jù)。微軟意識到了這個問題,因此添加了另一個socket選項:SO_EXCLUSIVEADDRUSE。對一個socket設置SO_EXCLUSIVEADDRUSE可以確保一旦該socket綁定了一個地址端口組合,任何其他socket,不論設置SO_REUSEADDR與否,都無法再綁定當前的地址端口組合。
Solaris
Solaris是SunOS的繼任者。SunOS從某種程度上來說也是一個較早版本的BSD的一個支路。因此Solaris只提供SO_REUSEADDR,且其表現(xiàn)和BSD系統(tǒng)中基本相同。據(jù)我所知,在Solaris系統(tǒng)中無法實現(xiàn)與SO_REUSEPORT相同的功能。這意味著在Solaris中無法將兩個socket綁定到完全相同的地址端口組合下。
與Windows類似的是,Solaris也為socket提供獨占綁定的選項——SO_EXCLBIND。如果一個socket在綁定地址前設置了這個選項,即使其他socket設置了SO_REUSEADDR也將無法綁定至相同地址。例如:如果socketA綁定在了通配符IP地址下,而socketB設置了SO_REUSEADDR且綁定在一個具體IP地址和與socketA相同的端口的組合下,這個操作在socketA沒有設置SO_EXCLBIND的情況下會成功,否則會失敗。
關(guān)于SO_REUSEPORT與SO_REUSEADDR的區(qū)別有哪些就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。
網(wǎng)站題目:SO_REUSEPORT與SO_REUSEADDR的區(qū)別有哪些-創(chuàng)新互聯(lián)
URL網(wǎng)址:http://www.rwnh.cn/article8/dscdip.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設計、網(wǎng)站營銷、網(wǎng)站建設、網(wǎng)站制作、網(wǎng)站導航、網(wǎng)站維護
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)