Skip to content

监听器系统

监听器系统提供所有入站协议使用的共享 TCP 和 UDP 监听器实现。

源码: common/listener/

TCP 监听器

go
type Listener struct {
    ctx          context.Context
    logger       logger.ContextLogger
    network      []string
    listenAddr   netip.AddrPort
    tcpListener  *net.TCPListener
    handler      adapter.ConnectionHandlerEx
    threadUnsafe bool
    // TLS, proxy protocol 等
}

功能

  • 监听地址: 绑定到特定的 IPv4/IPv6 地址和端口
  • TCP 选项: SO_REUSEADDR, TCP_FASTOPEN, TCP_DEFER_ACCEPT
  • Proxy Protocol: 支持 HAProxy proxy protocol v1/v2
  • 线程安全/非安全: 可选的单 goroutine 模式,用于需要它的协议

Accept 循环

go
func (l *Listener) loopTCPIn() {
    for {
        conn, err := l.tcpListener.AcceptTCP()
        if err != nil {
            return
        }
        // 如果配置了则应用 proxy protocol
        // 如果配置了则包装 TLS
        go l.handler.NewConnectionEx(ctx, conn, metadata, onClose)
    }
}

UDP 监听器

go
type UDPListener struct {
    ctx        context.Context
    logger     logger.ContextLogger
    listenAddr netip.AddrPort
    udpConn    *net.UDPConn
    handler    adapter.PacketHandlerEx
    // 用于 TProxy 的 OOB 处理器
}

功能

  • OOB 数据: 对于 TProxy,带外数据携带原始目标地址
  • 数据包处理器: 传递带有源地址的单个数据包

读取循环

go
func (l *UDPListener) loopUDPIn() {
    buffer := buf.NewPacket()
    for {
        n, addr, err := l.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
        if err != nil {
            return
        }
        buffer.Truncate(n)
        l.handler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr))
        buffer = buf.NewPacket()
    }
}

共享监听选项

go
type ListenOptions struct {
    Listen         ListenAddress
    ListenPort     uint16
    ListenFields   ListenFields
    TCPFastOpen    bool
    TCPMultiPath   bool
    UDPFragment    *bool
    UDPTimeout     Duration
    ProxyProtocol  bool
    ProxyProtocolAcceptNoHeader bool
    Detour         string
    InboundOptions
}

type InboundOptions struct {
    SniffEnabled              bool
    SniffOverrideDestination  bool
    SniffTimeout              Duration
    DomainStrategy            DomainStrategy
}

Proxy Protocol 支持

当设置 proxy_protocol: true 时,监听器使用 proxy protocol 解析来包装连接:

go
import proxyproto "github.com/pires/go-proxyproto"

listener = &proxyproto.Listener{
    Listener: tcpListener,
    Policy: func(upstream net.Addr) (proxyproto.Policy, error) {
        if acceptNoHeader {
            return proxyproto.USE, nil
        }
        return proxyproto.REQUIRE, nil
    },
}

这从负载均衡器/反向代理背后提取原始客户端地址。