端口重用
前言
服务器重启进程时总会提示端口已经被绑定的报错,直到重试好几次才能重启成功。
这是因为端口尚未完全关闭的情况,这时如果不设置端口重用,则无法完成绑定,因为端口还处于被别的套接口绑定的状态之中。
SO_REUSEADDR
简介
- 允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接仍存在。这通常是重启监听服务器时出现,若不设置此选项,则bind时将出错。
- 允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。对于TCP,我们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。
- 允许单个进程捆绑同一端口到多个套接口上,只要每个捆绑指定不同的本地IP地址即可。这一般不用于TCP服务器。
- 允许完全重复地捆绑:当一个IP地址和端口绑定到某个套接口上时,还允许此IP地址和端口捆绑到另一个套接口上。一般来说,这个特性仅在支持多播的系统上才有,而且只对UDP套接口而言(TCP不支持多播)。
Python中的用法
- 测试源码
copyimport 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)
- 不允许端口重用有如下报错:
copyTraceback (most recent call last):
File "C:\test.py", line 17, in <module>
sock2.bind(tcp_listen_addr)
OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
golang用法
- window方法
copypackage main
import (
"context"
"log"
"net"
"syscall"
)
func main() {
l := &net.ListenConfig{Control: reusePortControl}
s, err := l.Listen(context.Background(), "tcp", ":8080")
if err != nil {
log.Fatal(err)
}
defer s.Close()
for {
c, err := s.Accept()
if err != nil {
log.Fatal(err)
}
go func(c net.Conn) {
// 业务逻辑
}(c)
}
}
func reusePortControl(network, address string, c syscall.RawConn) error {
var opErr error
err := c.Control(func(fd uintptr) {
opErr = syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
})
if err != nil {
return err
}
return opErr
}
- Linux方法
copypackage main
import (
"context"
"log"
"net"
"syscall"
)
func main() {
l := &net.ListenConfig{Control: reusePortControl}
s, err := l.Listen(context.Background(), "tcp", ":8080")
if err != nil {
log.Fatal(err)
}
defer s.Close()
for {
c, err := s.Accept()
if err != nil {
log.Fatal(err)
}
go func(c net.Conn) {
// 业务逻辑
}(c)
}
}
func reusePortControl(network, address string, c syscall.RawConn) error {
var opErr error
err := c.Control(func(fd uintptr) {
// syscall.SO_REUSEPORT ,在Linux下还可以指定端口重用
opErr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)
})
if err != nil {
return err
}
return opErr
}
其他学习
关于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连接。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~