端口重用

前言

服務器重啓進程時總會提示端口已經被綁定的報錯,直到重試好幾回才能重啓成功。
這是由於端口還沒有徹底關閉的狀況,這時若是不設置端口重用,則沒法完成綁定,由於端口還處於被別的套接口綁定的狀態之中。python

SO_REUSEADDR

簡介

  1. 容許啓動一個監聽服務器並捆綁其衆所周知端口,即便之前創建的將此端口用作他們的本地端口的鏈接仍存在。這一般是重啓監聽服務器時出現,若不設置此選項,則bind時將出錯。
  2. 容許在同一端口上啓動同一服務器的多個實例,只要每一個實例捆綁一個不一樣的本地IP地址便可。對於TCP,咱們根本不可能啓動捆綁相同IP地址和相同端口號的多個服務器。
  3. 容許單個進程捆綁同一端口到多個套接口上,只要每一個捆綁指定不一樣的本地IP地址便可。這通常不用於TCP服務器。
  4. 容許徹底重複地捆綁:當一個IP地址和端口綁定到某個套接口上時,還容許此IP地址和端口捆綁到另外一個套接口上。通常來講,這個特性僅在支持多播的系統上纔有,並且只對UDP套接口而言(TCP不支持多播)。

Python中的用法

  1. 測試源碼
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)
  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源碼,編譯出來的可執行程序默認就支持端口重用。linux

  1. 修改源碼:\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
}
  1. 修改源碼:\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
}
  1. 修改完源碼,編譯時須要帶上-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

相關文章
相關標籤/搜索