Files
Meshray-Manager/core/engine.go
T
2026-06-30 15:14:37 +08:00

334 lines
9.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}