Initial commit

This commit is contained in:
2026-06-30 15:14:37 +08:00
commit 15dab96872
311 changed files with 95639 additions and 0 deletions
+358
View File
@@ -0,0 +1,358 @@
package connect
import (
"context"
"fmt"
"net"
"sync"
"time"
"github.com/pion/turn/v2"
"go.uber.org/zap"
)
// TURNProtocol TURN 协议类型
type TURNProtocol string
const (
TURNProtocolUDP TURNProtocol = "udp"
TURNProtocolTCP TURNProtocol = "tcp"
TURNProtocolTLS TURNProtocol = "tls"
)
// TURNFactory TURN 工厂(Layer 4-6: TURN-UDP/TCP/TLS
// 自包含实现:TURN 协议协商 + 建连
type TURNFactory struct {
protocol TURNProtocol
servers []string
username string
password string
logger *zap.Logger
}
// NewTURNFactory 创建 TURN 工厂
func NewTURNFactory(protocol TURNProtocol, servers []string, username, password string, logger *zap.Logger) *TURNFactory {
return &TURNFactory{
protocol: protocol,
servers: servers,
username: username,
password: password,
logger: logger,
}
}
// Layer 返回传输层类型
func (f *TURNFactory) Layer() Layer {
switch f.protocol {
case TURNProtocolUDP:
return LayerTURNUDP
case TURNProtocolTCP:
return LayerTURNTCP
case TURNProtocolTLS:
return LayerTURNTLS
default:
return LayerTURNUDP
}
}
// Name 返回名称
func (f *TURNFactory) Name() string {
switch f.protocol {
case TURNProtocolUDP:
return "TURN-UDP"
case TURNProtocolTCP:
return "TURN-TCP"
case TURNProtocolTLS:
return "TURN-TLS"
default:
return "TURN-UDP"
}
}
// Dial 建立 TURN 中继连接
func (f *TURNFactory) Dial(ctx context.Context, config *DialConfig) (net.Conn, error) {
f.logger.Info("开始建立 TURN 中继连接",
zap.String("peer_id", config.PeerID),
zap.String("protocol", string(f.protocol)))
servers := f.servers
if len(servers) == 0 {
servers = config.TURNServers
}
if len(servers) == 0 {
return nil, fmt.Errorf("未配置 TURN 服务器")
}
// 解析第一个 TURN 服务器
server := servers[0]
host, port := parseServerAddr(server)
// 根据协议类型建立连接
var relayConn net.PacketConn
var err error
switch f.protocol {
case TURNProtocolUDP:
relayConn, err = f.allocateUDP(ctx, host, port, config)
case TURNProtocolTCP:
relayConn, err = f.allocateTCP(ctx, host, port, config)
case TURNProtocolTLS:
return nil, fmt.Errorf("TURN-TLS 尚未实现")
default:
return nil, fmt.Errorf("不支持的 TURN 协议:%s", f.protocol)
}
if err != nil {
return nil, fmt.Errorf("TURN 分配失败:%w", err)
}
f.logger.Info("TURN 中继连接建立成功",
zap.String("peer_id", config.PeerID),
zap.String("relay_addr", relayConn.LocalAddr().String()))
// 包装成 net.Conn 返回
return newTURNConn(relayConn, f.logger), nil
}
// allocateUDP UDP TURN 分配
func (f *TURNFactory) allocateUDP(ctx context.Context, host, port string, config *DialConfig) (net.PacketConn, error) {
udpAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(host, port))
if err != nil {
return nil, fmt.Errorf("解析 UDP 地址失败:%w", err)
}
conn, err := net.DialUDP("udp", nil, udpAddr)
if err != nil {
return nil, fmt.Errorf("创建 UDP 连接失败:%w", err)
}
clientConfig := &turn.ClientConfig{
STUNServerAddr: net.JoinHostPort(host, port),
TURNServerAddr: net.JoinHostPort(host, port),
Username: f.username,
Password: f.password,
Conn: conn,
}
client, err := turn.NewClient(clientConfig)
if err != nil {
conn.Close()
return nil, fmt.Errorf("创建 TURN 客户端失败:%w", err)
}
if err := client.Listen(); err != nil {
client.Close()
conn.Close()
return nil, fmt.Errorf("TURN 客户端监听失败:%w", err)
}
relayConn, err := client.Allocate()
if err != nil {
client.Close()
conn.Close()
return nil, fmt.Errorf("分配 TURN 中继失败:%w", err)
}
// 创建 Permission(允许特定对端地址使用中继)
// 这是 TURN 协议的关键步骤,否则无法收发数据
// 注意:PeerID 在这里应该是对端的公网地址(由信使服务器转发)
if config != nil && config.PeerID != "" {
peerAddr, err := net.ResolveUDPAddr("udp", config.PeerID)
if err == nil {
if permErr := client.CreatePermission(peerAddr); permErr != nil {
f.logger.Warn("CreatePermission 失败",
zap.String("peer_addr", peerAddr.String()),
zap.Error(permErr))
// 注意:CreatePermission 失败不影响连接建立,只是警告
} else {
f.logger.Debug("CreatePermission 成功",
zap.String("peer_addr", peerAddr.String()))
}
}
}
f.logger.Debug("TURN-UDP 分配成功",
zap.String("relay_addr", relayConn.LocalAddr().String()))
return relayConn, nil
}
// allocateTCP TCP TURN 分配
func (f *TURNFactory) allocateTCP(ctx context.Context, host, port string, config *DialConfig) (net.PacketConn, error) {
dialer := &net.Dialer{Timeout: 10 * time.Second}
conn, err := dialer.DialContext(ctx, "tcp", net.JoinHostPort(host, port))
if err != nil {
return nil, fmt.Errorf("TCP 连接失败:%w", err)
}
packetConn := newTCPPacketConn(conn, f.logger)
clientConfig := &turn.ClientConfig{
STUNServerAddr: net.JoinHostPort(host, port),
TURNServerAddr: net.JoinHostPort(host, port),
Username: f.username,
Password: f.password,
Conn: packetConn,
}
client, err := turn.NewClient(clientConfig)
if err != nil {
conn.Close()
return nil, fmt.Errorf("创建 TURN 客户端失败:%w", err)
}
if err := client.Listen(); err != nil {
client.Close()
conn.Close()
return nil, fmt.Errorf("TURN 客户端监听失败:%w", err)
}
relayConn, err := client.Allocate()
if err != nil {
client.Close()
conn.Close()
return nil, fmt.Errorf("分配 TURN 中继失败:%w", err)
}
// 创建 Permission(允许特定对端地址使用中继)
if config != nil && config.PeerID != "" {
peerAddr, err := net.ResolveTCPAddr("tcp", config.PeerID)
if err == nil {
if permErr := client.CreatePermission(peerAddr); permErr != nil {
f.logger.Warn("CreatePermission 失败",
zap.String("peer_addr", peerAddr.String()),
zap.Error(permErr))
} else {
f.logger.Debug("CreatePermission 成功",
zap.String("peer_addr", peerAddr.String()))
}
}
}
f.logger.Debug("TURN-TCP 分配成功",
zap.String("relay_addr", relayConn.LocalAddr().String()))
return relayConn, nil
}
// parseServerAddr 解析服务器地址
func parseServerAddr(server string) (host, port string) {
h, p, _ := net.SplitHostPort(server)
if h == "" {
h = server
p = "3478" // 默认 TURN 端口
}
return h, p
}
// turnConn TURN 连接包装器
type turnConn struct {
relay net.PacketConn
remoteAddr net.Addr // 对端地址
buffer []byte
logger *zap.Logger
mu sync.Mutex
}
// newTURNConn 创建 TURN 连接
func newTURNConn(relay net.PacketConn, logger *zap.Logger) *turnConn {
return &turnConn{
relay: relay,
buffer: make([]byte, 65535),
logger: logger,
}
}
// SetRemoteAddr 设置对端地址(必须在 Write 之前调用)
func (c *turnConn) SetRemoteAddr(addr net.Addr) {
c.mu.Lock()
defer c.mu.Unlock()
c.remoteAddr = addr
}
func (c *turnConn) Read(b []byte) (n int, err error) {
c.mu.Lock()
defer c.mu.Unlock()
n, _, err = c.relay.ReadFrom(b)
return n, err
}
func (c *turnConn) Write(b []byte) (n int, err error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.remoteAddr == nil {
return 0, fmt.Errorf("未设置对端地址,请先调用 SetRemoteAddr()")
}
n, err = c.relay.WriteTo(b, c.remoteAddr)
return n, err
}
func (c *turnConn) Close() error {
return c.relay.Close()
}
func (c *turnConn) LocalAddr() net.Addr {
return c.relay.LocalAddr()
}
func (c *turnConn) RemoteAddr() net.Addr {
return nil // TURN 中继没有固定的 RemoteAddr
}
func (c *turnConn) SetDeadline(t time.Time) error {
return c.relay.SetDeadline(t)
}
func (c *turnConn) SetReadDeadline(t time.Time) error {
return c.relay.SetReadDeadline(t)
}
func (c *turnConn) SetWriteDeadline(t time.Time) error {
return c.relay.SetWriteDeadline(t)
}
// tcpPacketConn TCP PacketConn 包装器
type tcpPacketConn struct {
conn net.Conn
logger *zap.Logger
}
func newTCPPacketConn(conn net.Conn, logger *zap.Logger) *tcpPacketConn {
return &tcpPacketConn{
conn: conn,
logger: logger,
}
}
func (p *tcpPacketConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
n, err = p.conn.Read(b)
return n, p.conn.RemoteAddr(), err
}
func (p *tcpPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
return p.conn.Write(b)
}
func (p *tcpPacketConn) Close() error {
return p.conn.Close()
}
func (p *tcpPacketConn) LocalAddr() net.Addr {
return p.conn.LocalAddr()
}
func (p *tcpPacketConn) SetDeadline(t time.Time) error {
return p.conn.SetDeadline(t)
}
func (p *tcpPacketConn) SetReadDeadline(t time.Time) error {
return p.conn.SetReadDeadline(t)
}
func (p *tcpPacketConn) SetWriteDeadline(t time.Time) error {
return p.conn.SetWriteDeadline(t)
}