334 lines
9.5 KiB
Go
334 lines
9.5 KiB
Go
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
|
||
}
|