25

多个程序监听同一端口 - socket端口复用技术

 4 years ago
source link: https://studygolang.com/articles/29127
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

对于多个程序绑定同一个端口我们遇到最多的是(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())
        }
    }
}

执行该程序后发现多个进程可以绑定同一端口

EfY3UvN.png!web

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

socket man

相关问题:

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

欢迎关注我们的微信公众号,每天学习Go知识

FveQFjN.jpg!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK