https://www.jianshu.com/p/ce277812eca2
對於多個程序綁定同一個端口我們遇到最多的是(Port 80 was already in use),也就是說端口被占用,不能重復綁定,但是操作系統內核支持通過配置socket參數的方式來實現多個進程綁定同一個端口。
簡單示例
package main
import (
"context"
"golang.org/x/sys/windows"
"net"
"syscall"
)
var listenConfig = net.ListenConfig{
Control: MyControl,
}
func MyControl(network, address string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
err := windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1)
if err != nil {
panic(err)
}
})
}
func main() {
listener, err := listenConfig.Listen(context.Background(), "tcp", "127.0.0.1:8080")
if err != nil {
panic(err)
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err == nil {
println("new connection ", conn.RemoteAddr().String())
}
}
}
執行該程序后發現多個進程可以綁定同一端口
port_reuse.png
進程12720,3632和13612同時綁定了8080端口
原理解析
這個例子關鍵代碼是設置socket的SO_REUSEADDR參數
實現多個程序綁定一個端口windows下需要設置SO_REUSEADDR參數
linux下需要設置SO_REUSEADDR和SO_REUSEPORT參數
對於windows和linux系統SO_REUSEADDR參數含義並不相同
Windows系統:
using-so-reuseaddr-and-so-exclusiveaddruse
so-exclusiveaddruse
Linux系統:
The SO_REUSEPORT socket option
相關問題:
how-do-so-reuseaddr-and-so-reuseport-differ
可以看出Windows下SO_REUSEADDR可以用來端口復用,在Linux下SO_REUSEADDR為了實現TIME_WAIT階段的快速綁定,SO_REUSEPORT用來配置端口復用
安全問題
由此引發了一個安全問題,如果一個正常的web程序監聽80端口提供服務,其它惡意程序同樣監聽了80端口,那么發送到80端口的請求就會被惡意程序接收並處理,這是我們不願看到的。
Linux下處理方式為所有端口復用的進程必須在同一個用戶下
Windows下處理方式為添加SO_EXECLUSIVEADDRUSE參數,程序設置該參數后,其它程序就不能復用這個端口
SO_REUSEADDR: Allows a socket to bind to an address and port already in use. The SO_EXCLUSIVEADDRUSE option can prevent this.
開箱即用
c語言程序直接調用setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &reuse_port, sizeof(reuse_port));
函數即可
golang包greuse提供了開箱即用的端口復用功能,兼容多系統 https://github.com/gogf/greuse
其它語言程序可以通過調用bindp項目實現https://github.com/yongboy/bindp
作者:寫個代碼容易么
鏈接:https://www.jianshu.com/p/ce277812eca2
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。