https://blog.csdn.net/weibo1230123/article/details/79978745
https://blog.csdn.net/weixin_42157432/article/details/115560824
在linux socket網絡編程中,大規模并發TCP或UDP連接時,經常會用到端口復用:
int opt = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *) &opt, sizeof(opt))) {perror("setsockopt");return -1;
}
端口復用可以這樣理解:
在A機進行客戶端網絡編程,假如它使用的本地端口號為1234,如果沒有開啟端口復用的話,它用本地端口1234去連接B機再用本地端口連接C機時就不可以了。
若開啟了端口復用的話在本地端口1234訪問B機的情況下還可以用本地端口1234訪問C機。
如果是服務器程序中監聽的端口,即使開啟了復用,也不可以用該端口向外發起連接。
接下來看看setsockopt
函數的參數意義:
/* Set socket FD's option OPTNAME at protocol level LEVELto *OPTVAL (which is OPTLEN bytes long).Returns 0 on success, -1 for errors. */
extern int setsockopt (int __fd, int __level, int __optname,const void *__optval, socklen_t __optlen) __THROW;
__optname常見的有兩個:
SO_REUSEADDR
:地址復用
SO_REUSEPORT
:端口復用
一般來說,一個{addr,port}
只能被一個套接字綁定,無法重用。
不同的套接字只能綁定到不同的的{addr,port}
上
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套接口選項;
功能如下:
- 若監聽服務器進入TIME_WAIT狀態,可立即重啟
- 同一端口啟動同一服務器的多個實例,需要每個實例socket綁定不同的ip地址,一般需要多個網卡支持
- 支持完全重復的捆綁
當一個IP地址和端口綁定到某個socket上,還允許此IP地址和端口號捆綁到另一個socket上。
一般來說,這個特性僅在支持多播的系統上才有用,而且只針對UDPsocket,TCP不支持多播
對于監聽線程來說,可重用socket被稱為監聽桶(listener bucket),即每一個socket都是一個桶。以event模型為例,假設目前有3個子進程,嗎,每個進程中都有一個監聽線程和多個工作線程。
- 端口未重用情況下:
某個時刻,該監聽socket僅能由某一個進程持有,當該進程接收到請求后,才讓出監聽權,相當于各個監聽者只能輪流監聽。
- 端口重用情況下
這里我們重用地址和端口兩次,三個監聽者都可以同時監聽了。
三個監聽桶下,各個進程不用讓出監聽權,看上去減輕了互斥鎖的爭用,避免了饑餓,還能更高效地監聽,實現負載均衡,從而減輕了監聽線程的壓力,但是由于監聽的過程中需要消耗CPU,若是單核CPU是無法體現出端口復用的優勢的,反而會由于切換監聽線程而降低性能。
所以若要使用端口復用,需要考慮幾點: - 是否將監聽進程/線程隔離在各自CPU中
- 重用次數
- CPU核數