94 lines
2.2 KiB
Go
94 lines
2.2 KiB
Go
package connect
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"net"
|
||
"time"
|
||
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
// DirectFactory Direct-UDP 直连工厂(Layer 1)
|
||
type DirectFactory struct {
|
||
stunServers []string
|
||
logger *zap.Logger
|
||
}
|
||
|
||
// NewDirectFactory 创建 Direct-UDP 工厂
|
||
func NewDirectFactory(stunServers []string, logger *zap.Logger) *DirectFactory {
|
||
return &DirectFactory{
|
||
stunServers: stunServers,
|
||
logger: logger,
|
||
}
|
||
}
|
||
|
||
// Layer 返回传输层类型
|
||
func (f *DirectFactory) Layer() Layer {
|
||
return LayerDirectUDP
|
||
}
|
||
|
||
// Name 返回传输方式名称
|
||
func (f *DirectFactory) Name() string {
|
||
return "Direct-UDP"
|
||
}
|
||
|
||
// Dial 建立 Direct-UDP 直连
|
||
func (f *DirectFactory) Dial(ctx context.Context, config *DialConfig) (net.Conn, error) {
|
||
f.logger.Info("开始建立 Direct-UDP 直连",
|
||
zap.String("peer_id", config.PeerID))
|
||
|
||
servers := f.stunServers
|
||
if len(servers) == 0 {
|
||
servers = config.STUNServers
|
||
}
|
||
|
||
if len(servers) == 0 {
|
||
f.logger.Warn("未配置 STUN 服务器列表,仅尝试内部 P2P 打洞")
|
||
}
|
||
|
||
var candidates []string
|
||
if len(servers) > 0 {
|
||
// 1. 创建 STUN 客户端收集候选地址
|
||
stun := NewSTUNClient(servers, f.logger)
|
||
candidates = stun.CollectCandidates()
|
||
}
|
||
|
||
if len(candidates) == 0 {
|
||
f.logger.Warn("未能收集到任何 STUN 候选地址,回退至 PeerID")
|
||
// As a fallback, maybe PeerID contains IP:PORT
|
||
candidates = append(candidates, config.PeerID)
|
||
}
|
||
|
||
f.logger.Info("STUN 候选地址收集完成",
|
||
zap.Strings("candidates", candidates))
|
||
|
||
// 2. 实际 P2P 连接尝试
|
||
f.logger.Warn("当前尝试所有候选地址...")
|
||
|
||
dialer := &net.Dialer{
|
||
Timeout: 5 * time.Second,
|
||
}
|
||
|
||
// 建立 UDP 连接并返回最近可用的
|
||
var lastErr error
|
||
for _, candidate := range candidates {
|
||
if candidate == "" { continue }
|
||
|
||
conn, err := dialer.DialContext(ctx, "udp", candidate)
|
||
if err == nil {
|
||
f.logger.Info("Direct-UDP 直连建立成功",
|
||
zap.String("peer_id", config.PeerID),
|
||
zap.String("remote_addr", conn.RemoteAddr().String()))
|
||
return conn, nil
|
||
}
|
||
|
||
f.logger.Warn("候选地址连接失败",
|
||
zap.String("candidate", candidate),
|
||
zap.Error(err))
|
||
lastErr = err
|
||
}
|
||
|
||
return nil, fmt.Errorf("所有候选地址 UDP 连接均失败,最后错误:%w", lastErr)
|
||
}
|