端口重用

摘要:
SO_REUSEADDR简介允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接仍存在。我这边是通过修改go源码,编译出来的可执行程序默认就支持端口重用。因此实际项目中,可以由不同进程对同一个端口不同IP进行绑定。
目录

前言

服务器重启进程时总会提示端口已经被绑定的报错,直到重试好几次才能重启成功。
这是因为端口尚未完全关闭的情况,这时如果不设置端口重用,则无法完成绑定,因为端口还处于被别的套接口绑定的状态之中。

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:	est.py", line 17, in <module>
    sock2.bind(tcp_listen_addr)
OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。

golang用法

已经看了go源码,没能琢磨出不修改源码的方案,大家有办法搞定,记得给我说说额。
我这边是通过修改go源码,编译出来的可执行程序默认就支持端口重用。

  1. 修改源码:gosrc etsockopt_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. 修改源码:gosrc etsockopt_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”,操作系统知道只是两个不同的绑定。

SOCKET本方IP本方Port目的IP目的Port协议
sokcet1127.0.0.18000192.168.1.19000Tcp
socket2127.0.0.18000192.168.1.110000Tcp

总结

运用端口重用对于我来说最大的方便就是重启进程快了很多,不用一遍遍尝试绑定端口,都不知道啥时候可以成功。
还有就是通过学习,认识到建立监听的5个元素,只要其中一个不同,就能实例化多个socket连接。

免责声明:文章转载自《端口重用》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇wireshark筛选器汇总python限制进程、子进程占用内存大小、CPU时间的方法:resource模块下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Socket 编程中,TCP 流的结束标志与粘包问题

因为 TCP 本身是无边界的协议,因此它并没有结束标志,也无法分包。「包」的界定,是更上层的协议的事了(比如 HTTP)。 socket和文件不一样,从文件中读,读到末尾就到达流的结尾了,所以会返回-1或null,循环结束,但是socket是连接两个主机的桥梁,一端无法知道另一端到底还有没有数据要传输。socket如果不关闭的话,read之类的阻塞函数会一...

IDEA启动Tomcat服务器时某些端口(如1099端口)被占用的解决办法

启动Tomcat服务器时,出现1099端口被其它进程占用了解决办法:   1、找出占用1099端口的进程,进入windows命令,查看什么进程占用了1099端口,使用命令:netstat -aon|findstr 1099 找出占用1099端口的进程      2、然后关闭占用该端口的进程:taskkill -f -pid 3756 这样就可以正常启动To...

java网络编程,简单的客户端和服务器端

1.服务器端 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socke...

socket.io建立长连接

   socket.io是基于node.js,在命令行里输入npm socket.io下载模块,用node.js搭建后台 示例代码,客户端 1 <!DOCTYPE html> 2 <html lang="zh-CN"> 3 <head> 4 <meta charset="UTF-8"> 5...

dpdk test + dpdk-procinfo

https://www.geek-share.com/detail/2586671676.html ABI_VERSION buildtools devtools GNUmakefile MAINTAINERS mk app config...

《篡权的ss》linux命令五分钟系列之三十一

原文链接 本原创文章属于《Linux大棚》博客。 博客地址为http://roclinux.cn。 文章作者为roc。 === 上篇文章《和netstat说再见》中说到netstat已经被抛弃,取而代之的是ss命令。一些朋友在问“netstat为什么会被抛弃呢?ss又是什么命令呢?” 这篇文章,我们就来揭晓答案,重点说一说“篡权的ss”。 【作者粗心大意?...