传输层架构概述
简介
Xray-core 的传输层提供了一套可插拔的网络通信抽象。每种传输协议(TCP、WebSocket、gRPC、HTTPUpgrade、SplitHTTP、mKCP)在启动时将自身注册到全局注册表中,核心层根据配置分发拨号/监听调用。独立的安全层(TLS、REALITY)可以独立地包装任何传输协议。本文档涵盖注册表模式、核心接口、MemoryStreamConfig 配置对象,以及运行时如何选择传输协议。
关键接口
Dialer 接口
Dialer 接口(transport/internet/dialer.go:22-31)是出站连接的高层抽象:
type Dialer interface {
Dial(ctx context.Context, destination net.Destination) (stat.Connection, error)
DestIpAddress() net.IP
SetOutboundGateway(ctx context.Context, ob *session.Outbound)
}在内部,传输层为每个协议的拨号器使用更底层的函数签名:
// dialer.go:34
type dialFunc func(ctx context.Context, dest net.Destination, streamSettings *MemoryStreamConfig) (stat.Connection, error)Listener 接口
Listener 接口(transport/internet/tcp_hub.go:25-28)表示服务端监听器:
type Listener interface {
Close() error
Addr() net.Addr
}每种传输协议还注册一个 ListenFunc(tcp_hub.go:23):
type ListenFunc func(ctx context.Context, address net.Address, port net.Port,
settings *MemoryStreamConfig, handler ConnHandler) (Listener, error)其中 ConnHandler 的定义为 func(stat.Connection)。
SystemDialer 接口
SystemDialer(transport/internet/system_dialer.go:18-21)是进行操作系统级别实际连接的最底层抽象:
type SystemDialer interface {
Dial(ctx context.Context, source net.Address, destination net.Destination,
sockopt *SocketConfig) (net.Conn, error)
DestIpAddress() net.IP
}默认实现(DefaultSystemDialer)通过 RawConn.Control 应用套接字选项后调用 Go 的 net.Dialer.DialContext。
注册表模式
传输拨号器注册表
每种传输协议在 init() 函数中通过 RegisterTransportDialer(dialer.go:39-45)注册其拨号器:
var transportDialerCache = make(map[string]dialFunc)
func RegisterTransportDialer(protocol string, dialer dialFunc) error {
if _, found := transportDialerCache[protocol]; found {
return errors.New(protocol, " dialer already registered").AtError()
}
transportDialerCache[protocol] = dialer
return nil
}来自 transport/internet/tcp/dialer.go:110-112 的示例:
func init() {
common.Must(internet.RegisterTransportDialer(protocolName, Dial))
}传输监听器注册表
监听器通过 RegisterTransportListener(tcp_hub.go:13-19)以类似方式注册:
var transportListenerCache = make(map[string]ListenFunc)
func RegisterTransportListener(protocol string, listener ListenFunc) error {
if _, found := transportListenerCache[protocol]; found {
return errors.New(protocol, " listener already registered.").AtError()
}
transportListenerCache[protocol] = listener
return nil
}配置创建器注册表
每种传输协议还注册一个配置工厂(config.go:32-38):
var globalTransportConfigCreatorCache = make(map[string]ConfigCreator)
func RegisterProtocolConfigCreator(name string, creator ConfigCreator) error {
if _, found := globalTransportConfigCreatorCache[name]; found {
return errors.New("protocol ", name, " is already registered").AtError()
}
globalTransportConfigCreatorCache[name] = creator
return nil
}此函数在每种传输协议的 config.go 的 init() 函数中被调用,例如 transport/internet/tcp/config.go:8-12。
已注册的协议名称
| 传输协议 | 协议名称 | 拨号器文件 | 监听器文件 |
|---|---|---|---|
| TCP | "tcp" | tcp/dialer.go | tcp/hub.go |
| WebSocket | "websocket" | websocket/dialer.go | websocket/hub.go |
| gRPC | "grpc" | grpc/dial.go | grpc/hub.go |
| HTTPUpgrade | "httpupgrade" | httpupgrade/dialer.go | httpupgrade/hub.go |
| SplitHTTP | "splithttp" | splithttp/dialer.go | splithttp/hub.go |
| mKCP | "mkcp" | kcp/dialer.go | kcp/listener.go |
MemoryStreamConfig
MemoryStreamConfig(transport/internet/memory_settings.go:9-19)是 protobuf StreamConfig 的已解析内存形式,避免了运行时重复的 protobuf 反序列化:
type MemoryStreamConfig struct {
Destination *net.Destination
ProtocolName string
ProtocolSettings interface{}
SecurityType string
SecuritySettings interface{}
TcpmaskManager *finalmask.TcpmaskManager
UdpmaskManager *finalmask.UdpmaskManager
SocketSettings *SocketConfig
DownloadSettings *MemoryStreamConfig
}关键字段:
- ProtocolName:例如
"tcp"、"websocket"、"grpc"—— 决定使用哪个已注册的拨号器/监听器。 - ProtocolSettings:传输协议特定的配置(如
*tcp.Config、*websocket.Config),通过类型断言进行转换。 - SecurityType / SecuritySettings:
"tls"或"reality"及对应的配置对象。 - SocketSettings:底层套接字选项(
SocketConfigprotobuf)。 - DownloadSettings:SplitHTTP 用于单独下载流配置。
- TcpmaskManager / UdpmaskManager:数据包混淆层(finalmask)。
转换函数 ToMemoryStreamConfig(memory_settings.go:22-78)解析 protobuf StreamConfig:
func ToMemoryStreamConfig(s *StreamConfig) (*MemoryStreamConfig, error) {
ets, err := s.GetEffectiveTransportSettings()
// ... 从 StreamConfig 字段构建 MemoryStreamConfig
}传输协议选择流程
出站(拨号)
主入口点是 internet.Dial(dialer.go:48-75):
func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStreamConfig) (stat.Connection, error) {
if dest.Network == net.Network_TCP {
if streamSettings == nil {
s, _ := ToMemoryStreamConfig(nil)
streamSettings = s
}
protocol := streamSettings.ProtocolName
dialer := transportDialerCache[protocol]
if dialer == nil {
return nil, errors.New(protocol, " dialer not registered")
}
return dialer(ctx, dest, streamSettings)
}
if dest.Network == net.Network_UDP {
udpDialer := transportDialerCache["udp"]
// ...
}
}当 streamSettings 为 nil 时,ToMemoryStreamConfig(nil) 会生成默认配置,其中 ProtocolName = "tcp"(来自 config.go:58-64)。
入站(监听)
监听通过 ListenTCP 或 ListenUnix(tcp_hub.go:52-79)进行:
func ListenTCP(ctx context.Context, address net.Address, port net.Port,
settings *MemoryStreamConfig, handler ConnHandler) (Listener, error) {
// ...
protocol := settings.ProtocolName
listenFunc := transportListenerCache[protocol]
listener, err := listenFunc(ctx, address, port, settings, handler)
return listener, nil
}配置解析
StreamConfig.GetEffectiveProtocol 方法(config.go:58-64)默认返回 "tcp":
func (c *StreamConfig) GetEffectiveProtocol() string {
if c == nil || len(c.ProtocolName) == 0 {
return "tcp"
}
return c.ProtocolName
}传输协议特定的设置通过 GetTransportSettingsFor(config.go:71-81)获取,该方法遍历 TransportSettings 列表寻找匹配的协议名称。如果未找到,则通过已注册的 ConfigCreator 创建默认配置。
安全设置通过 GetEffectiveSecuritySettings(config.go:83-90)以类似方式解析。
架构图
flowchart TD
subgraph "应用层"
OUT[出站处理器]
IN[入站处理器]
end
subgraph "传输分发"
DIAL["internet.Dial()"]
LISTEN["internet.ListenTCP()"]
MSC[MemoryStreamConfig]
end
subgraph "注册表 (map[string]func)"
DR[transportDialerCache]
LR[transportListenerCache]
CR[globalTransportConfigCreatorCache]
end
subgraph "传输协议实现"
TCP["tcp.Dial / tcp.ListenTCP"]
WS["websocket.Dial / websocket.ListenWS"]
GRPC["grpc.Dial / grpc.Listen"]
HU["httpupgrade.Dial / httpupgrade.ListenHTTPUpgrade"]
SH["splithttp.Dial / splithttp.ListenXH"]
KCP["kcp.DialKCP / kcp.ListenKCP"]
end
subgraph "安全层"
TLS["tls.Client / tls.Server"]
REALITY["reality.UClient / reality.Server"]
end
subgraph "系统层"
SD["internet.DialSystem"]
SL["internet.ListenSystem"]
OS["OS net.Dialer / net.ListenConfig"]
end
OUT --> DIAL
IN --> LISTEN
DIAL --> MSC --> DR
LISTEN --> MSC --> LR
DR --> TCP & WS & GRPC & HU & SH & KCP
LR --> TCP & WS & GRPC & HU & SH & KCP
TCP --> TLS & REALITY
TCP --> SD
WS --> TLS
WS --> SD
GRPC --> TLS & REALITY
GRPC --> SD
HU --> TLS
HU --> SD
SH --> TLS & REALITY
SH --> SD
KCP --> TLS
KCP --> SL
SD --> OS
SL --> OSDomainStrategy 与 DNS 解析
在进行系统级连接之前,DialSystem(dialer.go:227-283)根据配置的 DomainStrategy 处理 DNS 解析。策略表(config.go:15-28)包含 11 种选项:
| 策略 | 行为 | 优先 | 回退 |
|---|---|---|---|
| AsIs | 不解析 | - | - |
| UseIP | 解析两种 | IPv4+IPv6 | 无 |
| UseIPv4 | 解析 v4 | IPv4 | 无 |
| UseIPv6 | 解析 v6 | IPv6 | 无 |
| UseIPv4v6 | 解析 v4,回退 v6 | IPv4 | IPv6 |
| UseIPv6v4 | 解析 v6,回退 v4 | IPv6 | IPv4 |
| ForceIP/v4/v6/v4v6/v6v4 | 同上但无结果时报错 | ... | ... |
当启用 Happy Eyeballs 且解析到多个 IP 时,将调用 TcpRaceDial 进行并发连接尝试(参见 TCP 传输)。
实现说明
- 线程安全:注册表映射(
transportDialerCache、transportListenerCache、globalTransportConfigCreatorCache)仅在init()期间写入,因此运行时读取不需要互斥锁。 - nil StreamConfig:向
Dial或ListenTCP传入nil会自动创建默认的 TCP 配置。 - DialerProxy:
DialSystem(dialer.go:271-280)支持通过sockopt.DialerProxy链接到另一个出站处理器,实现代理套代理的拓扑结构。 - AddressPortStrategy:
DialSystem可以使用 SRV 或 TXT DNS 记录覆盖目标地址/端口(dialer.go:139-224)。 - SystemDialer 替换:
UseAlternativeSystemDialer(system_dialer.go:232-237)允许替换整个系统拨号器,在 Android 等平台上使用。 - PacketConnWrapper:UDP "连接"实际上是
ListenPacket+WriteTo包装成net.Conn接口(system_dialer.go:152-204)。