Files
webRtc/Assets/PongStreamSender.cs

119 lines
4.1 KiB
C#
Raw Normal View History

2026-02-28 18:17:08 +08:00
using System;
using Unity.RenderStreaming.Signaling;
using UnityEngine;
using Unity.WebRTC;
namespace Unity.RenderStreaming.Samples
{
/// <summary>
/// 心跳接收通道 - 继承 DataChannelBase对应博客中的 ReceiveChannel
/// 接收服务端发送的 Ping回复 Pong
/// </summary>
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<long> OnPingReceived; // 参数RTT
public event Action OnTimeoutWarning; // 超时警告
private System.Collections.Generic.Queue<long> _latencyHistory = new System.Collections.Generic.Queue<long>(10);
[Serializable]
private class HeartbeatMessage
{
public int type; // 1=ping, 2=pong
public long timestamp; // 服务端发送时间戳
public int seq; // 序列号
}
/// <summary>
/// 重写 OnMessage - 当收到数据时自动调用(对应博客中的 ReceiveChannel 实现)
/// </summary>
protected override void OnMessage(byte[] bytes)
{
try
{
string json = System.Text.Encoding.UTF8.GetString(bytes);
var msg = JsonUtility.FromJson<HeartbeatMessage>(json);
if (msg.type == MSG_TYPE_PING)
{
HandlePing(msg);
}
}
catch (Exception e)
{
Debug.LogError($"[心跳接收通道] 消息解析失败: {e.Message}");
}
}
/// <summary>
/// 处理 Ping 消息并回复 Pong
/// </summary>
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);
}
/// <summary>
/// 发送 Pong 响应
/// </summary>
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}");
}
/// <summary>
/// 获取当前连接状态
/// </summary>
public bool IsConnected => Channel != null && Channel.ReadyState == RTCDataChannelState.Open;
}
}