目錄
- 前言
- SO_REUSEADDR
- 簡介
- Python中的用法
- golang用法
- 其他學習
- 總結
前言
服務器重啟行程時總會提示埠已經被系結的報錯,直到重試好幾次才能重啟成功,
這是因為埠尚未完全關閉的情況,這時如果不設定埠重用,則無法完成系結,因為埠還處于被別的套介面系結的狀態之中,
SO_REUSEADDR
簡介
- 允許啟動一個監聽服務器并捆綁其眾所周知埠,即使以前建立的將此埠用做他們的本地埠的連接仍存在,這通常是重啟監聽服務器時出現,若不設定此選項,則bind時將出錯,
- 允許在同一埠上啟動同一服務器的多個實體,只要每個實體捆綁一個不同的本地IP地址即可,對于TCP,我們根本不可能啟動捆綁相同IP地址和相同埠號的多個服務器,
- 允許單個行程捆綁同一埠到多個套介面上,只要每個捆綁指定不同的本地IP地址即可,這一般不用于TCP服務器,
- 允許完全重復地捆綁:當一個IP地址和埠系結到某個套介面上時,還允許此IP地址和埠捆綁到另一個套介面上,一般來說,這個特性僅在支持多播的系統上才有,而且只對UDP套介面而言(TCP不支持多播),
Python中的用法
- 測驗原始碼
import socket
serveripaddr = '127.0.0.1'
tcp_listen_addr = (serveripaddr, 12345)
set_reuse_addr = False # True:允許重用,不會報錯.False:默認不支持重用,會報錯
sock1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if set_reuse_addr:
sock1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock1.bind(tcp_listen_addr)
sock1.listen(1)
sock2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
if set_reuse_addr:
sock2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock2.bind(tcp_listen_addr)
sock2.listen(1)
- 不允許埠重用有如下報錯:
Traceback (most recent call last):
File "C:\test.py", line 17, in <module>
sock2.bind(tcp_listen_addr)
OSError: [WinError 10048] 通常每個套接字地址(協議/網路地址/埠)只允許使用一次,
golang用法
已經看了go原始碼,沒能琢磨出不修改原始碼的方案,大家有辦法搞定,記得給我說說額,
我這邊是通過修改go原始碼,編譯出來的可執行程式默認就支持埠重用,
- 修改原始碼:
\go\src\net\sockopt_windows.go,按照如下方法加入一段代碼,
func setDefaultSockopts(s syscall.Handle, family, sotype int, ipv6only bool) error {
if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
// Allow both IP versions even if the OS default
// is otherwise. Note that some operating systems
// never admit this option.
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only))
}
// 加入代碼,Start
if family == syscall.AF_INET && sotype == syscall.SOCK_STREAM {
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
}
// 加入代碼,End
if (sotype == syscall.SOCK_DGRAM || sotype == syscall.SOCK_RAW) && family != syscall.AF_UNIX && family != syscall.AF_INET6 {
// Allow broadcast.
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1))
}
return nil
}
- 修改原始碼:
\go\src\net\sockopt_linux.go,按照如下方法加入一段代碼,
func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
// Allow both IP versions even if the OS default
// is otherwise. Note that some operating systems
// never admit this option.
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only))
}
// 加入代碼,Start
if family == syscall.AF_INET && sotype == syscall.SOCK_STREAM {
syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
}
// 加入代碼,End
if (sotype == syscall.SOCK_DGRAM || sotype == syscall.SOCK_RAW) && family != syscall.AF_UNIX {
// Allow broadcast.
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1))
}
return nil
}
- 修改完原始碼,編譯時需要帶上
-a強制重新編譯所以包,不然還是會使用快取的.a檔案進行編譯,例如:go build -a test.go,
其他學習
關于socket有如下5個重要元素,只要其中一個不同,那系統就能區別不同的socket連接,只要5個完全相同,則后面建立系結的代碼會報報錯,
因此實際專案中,可以由不同行程對同一個埠不同IP進行系結,例如可以同時系結“127.0.0.0:12345”和“192.168.1.10:12345”,作業系統知道只是兩個不同的系結,
| SOCKET | 本方IP | 本方Port | 目的IP | 目的Port | 協議 |
|---|---|---|---|---|---|
| sokcet1 | 127.0.0.1 | 8000 | 192.168.1.1 | 9000 | Tcp |
| socket2 | 127.0.0.1 | 8000 | 192.168.1.1 | 10000 | Tcp |
總結
運用埠重用對于我來說最大的方便就是重啟行程快了很多,不用一遍遍嘗試系結埠,都不知道啥時候可以成功,
還有就是通過學習,認識到建立監聽的5個元素,只要其中一個不同,就能實體化多個socket連接,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/212670.html
標籤:Go
