From 6b0a434483a9bd65f097498f58268c575620dde6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=BA=E8=83=BD=E5=A4=A7=E7=9F=B3=E5=A4=B4?= Date: Sun, 28 Apr 2024 00:11:25 +0800 Subject: [PATCH] =?UTF-8?q?WebSocket=E5=AE=A2=E6=88=B7=E7=AB=AF=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E7=AB=AF=E5=A2=9E=E5=8A=A0=E5=BF=83=E8=B7=B3PingPong?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8C=E9=BB=98=E8=AE=A4120=E7=A7=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NewLife.Core/Data/Packet.cs | 7 +++++- NewLife.Core/Http/WebSocket.cs | 6 ++++- NewLife.Core/Net/WebSocketClient.cs | 36 +++++++++++++++++++++++++++ Samples/Zero.HttpServer/ClientTest.cs | 2 ++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/NewLife.Core/Data/Packet.cs b/NewLife.Core/Data/Packet.cs index 2a2c4d9cf..b2b974ee4 100644 --- a/NewLife.Core/Data/Packet.cs +++ b/NewLife.Core/Data/Packet.cs @@ -493,13 +493,18 @@ public String ToBase64() /// 重载类型转换,字节数组直接转为Packet对象 /// /// - public static implicit operator Packet(Byte[] value) => value == null ? null! : new Packet(value); + public static implicit operator Packet(Byte[] value) => value == null ? null! : new(value); /// 重载类型转换,一维数组直接转为Packet对象 /// /// public static implicit operator Packet(ArraySegment value) => new(value); + /// 重载类型转换,字符串直接转为Packet对象 + /// + /// + public static implicit operator Packet(String value) => new(value.GetBytes()); + /// 已重载 /// public override String ToString() => $"[{Data.Length}]({Offset}, {Count})" + (Next == null ? "" : $"<{Total}>"); diff --git a/NewLife.Core/Http/WebSocket.cs b/NewLife.Core/Http/WebSocket.cs index eb397d31d..35c7cdbd3 100644 --- a/NewLife.Core/Http/WebSocket.cs +++ b/NewLife.Core/Http/WebSocket.cs @@ -87,7 +87,11 @@ public void Process(Packet pk) break; case WebSocketMessageType.Ping: { - var msg = new WebSocketMessage { Type = WebSocketMessageType.Pong, MaskKey = Rand.NextBytes(4) }; + var msg = new WebSocketMessage + { + Type = WebSocketMessageType.Pong, + Payload = $"Pong {DateTime.UtcNow.ToFullString()}", + }; session.Send(msg.ToPacket()); } break; diff --git a/NewLife.Core/Net/WebSocketClient.cs b/NewLife.Core/Net/WebSocketClient.cs index 1933b052b..8c1812448 100644 --- a/NewLife.Core/Net/WebSocketClient.cs +++ b/NewLife.Core/Net/WebSocketClient.cs @@ -5,6 +5,7 @@ using NewLife.Log; using NewLife.Net.Handlers; using NewLife.Security; +using NewLife.Threading; #if !NET45 using TaskEx = System.Threading.Tasks.Task; #endif @@ -17,6 +18,9 @@ public class WebSocketClient : TcpSession #region 属性 /// 资源地址 public Uri Uri { get; set; } = null!; + + /// WebSocket心跳间隔。默认60秒 + public TimeSpan KeepAlive { get; set; } = TimeSpan.FromSeconds(120); #endregion #region 构造 @@ -63,9 +67,24 @@ protected override Boolean OnOpen() //Active = false; + var p = (Int32)KeepAlive.TotalMilliseconds; + if (p > 0) + _timer = new TimerX(DoPing, null, 5_000, p) { Async = true }; + return true; } + /// 关闭连接 + /// + /// + protected override Boolean OnClose(String reason) + { + _timer.TryDispose(); + _timer = null; + + return base.OnClose(reason); + } + #region 消息收发 /// 接收WebSocket消息 /// @@ -148,6 +167,23 @@ public Task CloseAsync(Int32 closeStatus, String? statusDescription = null, Canc } #endregion + #region 心跳 + private TimerX? _timer; + private void DoPing(Object? state) + { + var msg = new WebSocketMessage + { + Type = WebSocketMessageType.Ping, + Payload = $"Ping {DateTime.UtcNow.ToFullString()}", + }; + + SendMessage(msg); + + var p = (Int32)KeepAlive.TotalMilliseconds; + if (_timer != null) _timer.Period = p; + } + #endregion + #region 辅助 /// 握手 /// diff --git a/Samples/Zero.HttpServer/ClientTest.cs b/Samples/Zero.HttpServer/ClientTest.cs index e8d84c4a5..6b4e35701 100644 --- a/Samples/Zero.HttpServer/ClientTest.cs +++ b/Samples/Zero.HttpServer/ClientTest.cs @@ -80,6 +80,8 @@ public static async Task WebSocketClientTest() var rs = await client.ReceiveMessageAsync(default); client.WriteLog(rs.Payload.ToStr()); + await Task.Delay(6_000); + // 关闭连接 await client.CloseAsync(1000, "通信完成", default); client.WriteLog("Close");