服务器重启进程时总会提示端口已经被绑定的报错,直到重试好几回才能重启成功。
这是由于端口还没有彻底关闭的状况,这时若是不设置端口重用,则没法完成绑定,由于端口还处于被别的套接口绑定的状态之中。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