using System; using Unity.RenderStreaming.Signaling; using UnityEngine; using Unity.WebRTC; namespace Unity.RenderStreaming.Samples { /// /// 心跳接收通道 - 继承 DataChannelBase(对应博客中的 ReceiveChannel) /// 接收服务端发送的 Ping,回复 Pong /// class HeartbeatReceiveChannel : DataChannelBase { // 消息类型(必须与服务端保持一致) private const int MSG_TYPE_PING = 1; private const int MSG_TYPE_PONG = 2; // 统计信息 public long LastRtt { get; private set; } public float AverageRtt { get; private set; } public int ReceivedPingCount { get; private set; } // 事件 public event Action OnPingReceived; // 参数:RTT public event Action OnTimeoutWarning; // 超时警告 private System.Collections.Generic.Queue _latencyHistory = new System.Collections.Generic.Queue(10); [Serializable] private class HeartbeatMessage { public int type; // 1=ping, 2=pong public long timestamp; // 服务端发送时间戳 public int seq; // 序列号 } /// /// 重写 OnMessage - 当收到数据时自动调用(对应博客中的 ReceiveChannel 实现) /// protected override void OnMessage(byte[] bytes) { try { string json = System.Text.Encoding.UTF8.GetString(bytes); var msg = JsonUtility.FromJson(json); if (msg.type == MSG_TYPE_PING) { HandlePing(msg); } } catch (Exception e) { Debug.LogError($"[心跳接收通道] 消息解析失败: {e.Message}"); } } /// /// 处理 Ping 消息并回复 Pong /// private void HandlePing(HeartbeatMessage ping) { long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); long latency = now - ping.timestamp; ReceivedPingCount++; LastRtt = latency; // 更新平均延迟 _latencyHistory.Enqueue(latency); if (_latencyHistory.Count > 10) _latencyHistory.Dequeue(); long sum = 0; foreach (var lat in _latencyHistory) sum += lat; AverageRtt = sum / (float)_latencyHistory.Count; Debug.Log($"[心跳接收通道] 收到 Ping #{ping.seq}, 延迟: {latency}ms, 平均: {AverageRtt:F1}ms"); // 立即回复 Pong(关键:回传原始时间戳,方便服务端计算 RTT) SendPong(ping.timestamp, ping.seq); // 触发事件 OnPingReceived?.Invoke(latency); } /// /// 发送 Pong 响应 /// private void SendPong(long originalTimestamp, int seq) { if (Channel == null || Channel.ReadyState != RTCDataChannelState.Open) { Debug.LogWarning("[心跳接收通道] 通道未打开,无法发送 Pong"); return; } var pong = new HeartbeatMessage { type = MSG_TYPE_PONG, timestamp = originalTimestamp, // 回传服务端的时间戳 seq = seq }; string json = JsonUtility.ToJson(pong); byte[] bytes = System.Text.Encoding.UTF8.GetBytes(json); Channel.Send(bytes); Debug.Log($"[心跳接收通道] 发送 Pong #{seq}"); } /// /// 获取当前连接状态 /// public bool IsConnected => Channel != null && Channel.ReadyState == RTCDataChannelState.Open; } }