2020-04-10 16:32:33 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using LeanCloud.Common;
|
|
|
|
|
using LeanCloud.Realtime.Protocol;
|
|
|
|
|
|
|
|
|
|
namespace LeanCloud.Realtime.Internal.Connection {
|
|
|
|
|
/// <summary>
|
2020-04-16 11:38:22 +08:00
|
|
|
|
/// 心跳控制器,由于 .Net Standard 2.0 不支持发送 ping frame,所以需要发送逻辑心跳
|
2020-04-10 16:32:33 +08:00
|
|
|
|
/// 1. 每次接收到消息后开始监听,如果在 pingInterval 时间内没有再次接收到消息,则发送 ping 请求;
|
|
|
|
|
/// 2. 发送后等待 pongInterval 时间,如果在此时间内接收到了任何消息,则取消并重新开始监听 1;
|
|
|
|
|
/// 3. 如果没收到消息,则认为超时并回调,连接层接收回调后放弃当前连接,以断线逻辑处理
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal class LCHeartBeat {
|
|
|
|
|
private readonly LCConnection connection;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// ping 间隔
|
|
|
|
|
/// </summary>
|
|
|
|
|
private readonly int pingInterval;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// pong 间隔
|
|
|
|
|
/// </summary>
|
|
|
|
|
private readonly int pongInterval;
|
|
|
|
|
|
|
|
|
|
private CancellationTokenSource pingCTS;
|
|
|
|
|
private CancellationTokenSource pongCTS;
|
|
|
|
|
|
|
|
|
|
internal LCHeartBeat(LCConnection connection,
|
|
|
|
|
int pingInterval,
|
2020-04-13 17:29:55 +08:00
|
|
|
|
int pongInterval) {
|
2020-04-10 16:32:33 +08:00
|
|
|
|
this.connection = connection;
|
|
|
|
|
this.pingInterval = pingInterval;
|
|
|
|
|
this.pongInterval = pongInterval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 更新心跳监听
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2020-04-13 17:29:55 +08:00
|
|
|
|
internal async Task Update(Action onTimeout) {
|
2020-04-10 16:32:33 +08:00
|
|
|
|
LCLogger.Debug("HeartBeat update");
|
|
|
|
|
pingCTS?.Cancel();
|
|
|
|
|
pongCTS?.Cancel();
|
|
|
|
|
|
|
|
|
|
// 计时准备 ping
|
|
|
|
|
pingCTS = new CancellationTokenSource();
|
|
|
|
|
Task delayTask = Task.Delay(pingInterval, pingCTS.Token);
|
|
|
|
|
await delayTask;
|
|
|
|
|
if (delayTask.IsCanceled) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 发送 ping 包
|
|
|
|
|
LCLogger.Debug("Ping ~~~");
|
|
|
|
|
GenericCommand command = new GenericCommand {
|
|
|
|
|
Cmd = CommandType.Echo,
|
|
|
|
|
AppId = LCApplication.AppId,
|
|
|
|
|
PeerId = connection.id
|
|
|
|
|
};
|
|
|
|
|
_ = connection.SendRequest(command);
|
|
|
|
|
pongCTS = new CancellationTokenSource();
|
|
|
|
|
Task timeoutTask = Task.Delay(pongInterval, pongCTS.Token);
|
|
|
|
|
await timeoutTask;
|
|
|
|
|
if (timeoutTask.IsCanceled) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// timeout
|
|
|
|
|
LCLogger.Error("Ping timeout");
|
|
|
|
|
onTimeout.Invoke();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 停止心跳监听
|
|
|
|
|
/// </summary>
|
|
|
|
|
internal void Stop() {
|
|
|
|
|
pingCTS?.Cancel();
|
|
|
|
|
pongCTS?.Cancel();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|