服務器重啓進程時總會提示端口已經被綁定的報錯,直到重試好幾回才能重啓成功。
這是由於端口還沒有徹底關閉的狀況,這時若是不設置端口重用,則沒法完成綁定,由於端口還處於被別的套接口綁定的狀態之中。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] 一般每一個套接字地址(協議/網絡地址/端口)只容許使用一次。
已經看了go源碼,沒能琢磨出不修改源碼的方案,你們有辦法搞定,記得給我說說額。
我這邊是經過修改go源碼,編譯出來的可執行程序默認就支持端口重用。linux
\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」,操做系統知道只是兩個不一樣的綁定。golang
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鏈接。windows