package core import ( "context" "fmt" "net" "time" "git.zkcoi.com/zkcoi/meshray/core/connect" "git.zkcoi.com/zkcoi/meshray/core/plugins/wg" "git.zkcoi.com/zkcoi/meshray/core/transport" "go.uber.org/zap" ) // Engine 引擎实例 - 一个组网的引擎实例 type Engine struct { logger *zap.Logger scheduler *connect.StrategyScheduler connMgr *transport.ConnManager relay *transport.Relay plugin transport.ProtocolPlugin metrics *Metrics // 候选地址存储(用于 NotifyPeerInfo) candidateStore map[string][]Candidate routeIDStore map[string]uint32 } // NewEngine 创建引擎实例 func NewEngine(logger *zap.Logger, metrics *Metrics) *Engine { // 创建 WG 插件 plugin := wg.NewWGPlugin() // 创建连接管理器 connMgr := transport.NewConnManager(logger) // 创建数据转发器 relay := transport.NewRelay(plugin, connMgr, logger) // 创建策略调度器 scheduler := connect.NewStrategyScheduler(logger) scheduler.OnConnectionUpdate = func(peerID string, conn net.Conn, err error) { if err != nil { logger.Warn("收到策略调度器连接错误更新", zap.String("peer_id", peerID), zap.Error(err)) } if conn != nil { logger.Info("策略调度器连接已建立,开始双向转发", zap.String("peer_id", peerID)) connMgr.Add(peerID, conn) // 启动远端接收协程 relay.StartReadFromRemoteConn(context.Background(), peerID, conn) } } scheduler.RegisterFactory(connect.NewDirectFactory(nil, logger)) // 1. Direct-UDP scheduler.RegisterFactory(connect.NewFakeTCPFactory(logger)) // 2. FakeTCP scheduler.RegisterFactory(connect.NewRealTCPFactory(logger)) // 3. RealTCP scheduler.RegisterFactory(connect.NewTURNFactory(connect.TURNProtocolUDP, nil, "", "", logger)) // 4. TURN-UDP // scheduler.RegisterFactory(connect.NewTURNFactoryQUIC(nil, "", "", logger)) // 5. TURN-QUIC (暂不启用) scheduler.RegisterFactory(connect.NewTURNFactory(connect.TURNProtocolTCP, nil, "", "", logger)) // 6. TURN-TCP scheduler.RegisterFactory(connect.NewTURNFactory(connect.TURNProtocolTLS, nil, "", "", logger)) // 7. TURN-TLS scheduler.RegisterFactory(connect.NewWebRTCFactory(&connect.ICEConfig{}, logger)) // 8. WebRTC scheduler.RegisterFactory(connect.NewWSFactory(nil, logger)) // 9. WS/WSS engine := &Engine{ logger: logger, scheduler: scheduler, connMgr: connMgr, relay: relay, plugin: plugin, metrics: metrics, candidateStore: make(map[string][]Candidate), routeIDStore: make(map[string]uint32), } // 设置拨号触发器:当 WG 发包但没连接时自动 9 层拨号 relay.OnDialTrigger = func(peerKey string) { go engine.initiateConnection(peerKey) } return engine } // Start 启动引擎 func (e *Engine) Start() error { e.logger.Info("Core 引擎启动") return nil } // Stop 停止引擎 func (e *Engine) Stop() error { e.logger.Info("Core 引擎停止") // 关闭所有连接 if e.connMgr != nil { e.connMgr.CloseAll() } return nil } // SetICEConfig 设置 ICE 配置(用于 WebRTC) func (e *Engine) SetICEConfig(config connect.ICEConfig) error { e.logger.Info("更新 ICE 配置", zap.Int("stun_servers", len(config.STUNServers)), zap.Int("turn_servers", len(config.TURNServers))) // TODO: 实现 ICE 配置更新逻辑 // 1. 找到 WebRTC 工厂 // 2. 更新其 ICE 配置 // 3. 重新注册工厂 // 目前先记录日志,P3 阶段实现 e.logger.Warn("SetICEConfig 暂未实现,将在 P3 阶段完成") return nil } // GetScheduler 获取策略调度器 func (e *Engine) GetScheduler() *connect.StrategyScheduler { return e.scheduler } // GetConnMgr 获取连接管理器 func (e *Engine) GetConnMgr() *transport.ConnManager { return e.connMgr } // GetRelay 获取数据转发器 func (e *Engine) GetRelay() *transport.Relay { return e.relay } // GetMetrics 获取监控指标 func (e *Engine) GetMetrics() *Metrics { return e.metrics } // Bind 为指定 Peer 开启本地端口,开始建连 // peerKey: 对端公钥哈希(8 字符) // localPort: 本地监听端口(传 0 表示系统自动分配) // 返回值:实际绑定的端口号 func (e *Engine) Bind(peerKey string, localPort int) (int, error) { // 1. 在本地端口监听 addr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: localPort} conn, err := net.ListenUDP("udp", addr) if err != nil { return 0, fmt.Errorf("监听本地端口失败:%w", err) } actualPort := conn.LocalAddr().(*net.UDPAddr).Port // 2. 提取 route_id(从 peerKey 派生) routeID := extractRouteID(peerKey) // 3. 注册到 Relay e.relay.RegisterLocalPort(routeID, conn) // 4. 注册到 ConnManager e.connMgr.Add(peerKey, nil) // conn 初始为 nil,建连后设置 // 5. 启动读取协程 ctx := context.Background() e.relay.StartReadFromLocalPort(ctx, routeID, peerKey) e.logger.Info("Bind 成功", zap.String("peer_key", peerKey), zap.Int("local_port", actualPort), zap.Uint32("route_id", routeID)) return actualPort, nil } // Unbind 停止指定 Peer 的端口监听 func (e *Engine) Unbind(peerKey string) error { // 1. 提取 route_id routeID := extractRouteID(peerKey) // 2. 从 Relay 注销 e.relay.UnregisterLocalPort(routeID) // 3. 从 ConnManager 移除 e.connMgr.Remove(peerKey) // 4. 清理存储 delete(e.candidateStore, peerKey) delete(e.routeIDStore, peerKey) e.logger.Info("Unbind 成功", zap.String("peer_key", peerKey), zap.Uint32("route_id", routeID)) return nil } // EngineStatus Engine 状态 type EngineStatus struct { PeerCount int `json:"peer_count"` Peers map[string]*PeerStatus `json:"peers"` // Metrics ActiveConnections int64 `json:"active_connections"` TotalConnections int64 `json:"total_connections"` BytesSent uint64 `json:"bytes_sent"` BytesReceived uint64 `json:"bytes_received"` StrategyFallbacks int64 `json:"strategy_fallbacks"` LastSwitchTime int64 `json:"last_switch_time"` // unix timestamp } // PeerStatus Peer 状态 type PeerStatus struct { PeerKey string `json:"peer_key"` Connected bool `json:"connected"` Layer string `json:"layer,omitempty"` // 当前传输层 } // GetStatus 查询 Engine 状态 func (e *Engine) GetStatus() (*EngineStatus, error) { status := &EngineStatus{ PeerCount: e.connMgr.Count(), Peers: make(map[string]*PeerStatus), } // 收集 Metrics if e.metrics != nil { status.ActiveConnections = e.metrics.GetActiveConnections() status.TotalConnections = e.metrics.GetTotalConnections() status.BytesSent = e.metrics.GetBytesSent() status.BytesReceived = e.metrics.GetBytesReceived() status.StrategyFallbacks = e.metrics.GetStrategyFallbacks() status.LastSwitchTime = e.metrics.GetLastSwitchTime().Unix() } // 收集所有 Peer 状态 for peerKey, conn := range e.connMgr.GetAll() { peerStatus := &PeerStatus{ PeerKey: peerKey, Connected: conn != nil, } if conn != nil { peerStatus.Layer = e.scheduler.GetPeerLayer(peerKey).String() } status.Peers[peerKey] = peerStatus } return status, nil } // Candidate 候选地址(与 connect.Candidate 对齐) type Candidate struct { Addr string `json:"addr"` // 候选地址(ip:port) Type string `json:"type"` // 候选类型:host/srflx/relay Priority int `json:"priority"` // 优先级 Protocol string `json:"protocol"` // 协议:udp/tcp } // NotifyPeerInfo 下发对端候选地址和 route_id // peerKey: 对端公钥哈希 // candidates: 对端候选地址列表(由信使服务器转发) // routeID: 路由 ID(用于数据转发) func (e *Engine) NotifyPeerInfo(peerKey string, candidates []Candidate, routeID uint32) error { // 1. 存储候选地址(用于后续建连) e.candidateStore[peerKey] = candidates // 2. 存储 route_id 映射 e.routeIDStore[peerKey] = routeID // 3. 触发建连流程 go e.initiateConnection(peerKey) e.logger.Info("NotifyPeerInfo 成功", zap.String("peer_key", peerKey), zap.Int("candidate_count", len(candidates)), zap.Uint32("route_id", routeID)) return nil } // initiateConnection 触发建连流程 func (e *Engine) initiateConnection(peerKey string) { // 1. 获取候选地址 candidates := e.candidateStore[peerKey] if len(candidates) == 0 { return } // 2. 检查是否已经在拨号或已连接 if conn, ok := e.connMgr.Get(peerKey); ok && conn != nil { return } // 2. 初始化 DialConfig config := &connect.DialConfig{ PeerID: peerKey, Timeout: 10 * time.Second, Logger: e.logger, } e.logger.Info("开始建立连接到对端", zap.String("peer_key", peerKey), zap.Int("candidate_count", len(candidates))) // 3. 获取 RouteID _, ok := e.routeIDStore[peerKey] if !ok { e.logger.Warn("未找到 route_id", zap.String("peer_key", peerKey)) return } // 4. 使用策略调度器尝试建连 conn, err := e.scheduler.Dial(config) if err != nil { e.logger.Error("所有策略层尝试连接均失败", zap.String("peer_key", peerKey), zap.Error(err)) return } // 5. 连接成功,更新到 ConnManager e.connMgr.Add(peerKey, conn) e.logger.Info("连接建立并更新成功", zap.String("peer_key", peerKey)) } // extractRouteID 从 peerKey 提取 route_id(简化版本) // 实际应该使用一致的哈希算法 func extractRouteID(peerKey string) uint32 { // 简单哈希:取前 4 个字符的 ASCII 码和 var sum uint32 = 0 for i := 0; i < len(peerKey) && i < 4; i++ { sum += uint32(peerKey[i]) } return sum }