diff --git a/.github/workflows/publish-beta.yml b/.github/workflows/publish-beta.yml index ed2808e18..4eeed0347 100644 --- a/.github/workflows/publish-beta.yml +++ b/.github/workflows/publish-beta.yml @@ -2,11 +2,11 @@ name: publish-beta on: push: - branches: [ master, dev, v10 ] + branches: [ master, dev ] paths: - 'NewLife.Core/**' pull_request: - branches: [ master, dev, v10 ] + branches: [ master, dev ] paths: - 'NewLife.Core/**' workflow_dispatch: diff --git a/NewLife.Core/Data/DbTable.cs b/NewLife.Core/Data/DbTable.cs index 33e897132..d672bf99c 100644 --- a/NewLife.Core/Data/DbTable.cs +++ b/NewLife.Core/Data/DbTable.cs @@ -415,6 +415,8 @@ public void WriteData(Binary bn, Int32[] fields) /// public IPacket ToPacket() { + // 不确定所需大小,只能使用内存流,再包装为数据包。 + // 头部预留8个字节,方便网络传输时添加协议头。 var ms = new MemoryStream { Position = 8 @@ -423,6 +425,8 @@ public IPacket ToPacket() Write(ms); ms.Position = 8; + + // 包装为数据包,直接窃取内存流内部的缓冲区 return new ArrayPacket(ms); } diff --git a/NewLife.Core/Data/IPacket.cs b/NewLife.Core/Data/IPacket.cs index 34b8d335f..64be95159 100644 --- a/NewLife.Core/Data/IPacket.cs +++ b/NewLife.Core/Data/IPacket.cs @@ -50,6 +50,13 @@ public interface IPacket IPacket Slice(Int32 offset, Int32 count = -1); } +/// 拥有管理权的数据包。使用完以后需要释放 +public interface IOwnerPacket : IPacket, IDisposable +{ + /// 是否拥有管理权 + Boolean HasOwner { get; set; } +} + /// 内存包辅助类 public static class PacketHelper { @@ -247,7 +254,7 @@ public static Byte[] ReadBytes(this IPacket pk, Int32 offset = 0, Int32 count = /// /// 使用时务必明确所有权归属,用完后及时释放。 /// -public struct OwnerPacket : IDisposable, IPacket +public struct OwnerPacket : IDisposable, IPacket, IOwnerPacket { #region 属性 private IMemoryOwner _owner; @@ -427,7 +434,7 @@ public IPacket Slice(Int32 offset, Int32 count) } /// 字节数组包 -public struct ArrayPacket : IDisposable, IPacket +public struct ArrayPacket : IDisposable, IPacket, IOwnerPacket { #region 属性 private Byte[] _buffer; @@ -455,6 +462,9 @@ public struct ArrayPacket : IDisposable, IPacket /// 总长度 public Int32 Total => Length + (Next?.Total ?? 0); + + /// 空数组 + public static ArrayPacket Empty = new([]); #endregion #region 索引 @@ -504,6 +514,7 @@ public Byte this[Int32 index] } #endregion + #region 构造 /// 通过指定字节数组来实例化数据包 /// /// @@ -563,11 +574,8 @@ public ArrayPacket(Stream stream) #endif } - //Set(stream.ToArray()); - var buf = new Byte[stream.Length - stream.Position]; var count = stream.Read(buf, 0, buf.Length); - //Set(buf, 0, count); _buffer = buf; _offset = 0; _length = count; @@ -587,6 +595,7 @@ public void Dispose() HasOwner = false; } } + #endregion /// 获取分片包。在管理权生命周期内短暂使用 /// @@ -603,16 +612,18 @@ public void Dispose() /// /// 偏移 /// 个数。默认-1表示到末尾 - public IPacket Slice(Int32 offset, Int32 count) - { - //var remain = _length - offset; - //if (count < 0 || count > remain) count = remain; - //if (offset == 0 && count == _length) return this; + public ArrayPacket Slice(Int32 offset, Int32 count) => (ArrayPacket)(this as IPacket).Slice(offset, count); - //var pk = new ArrayPacket(_buffer, _offset + offset, count) { HasOwner = HasOwner }; - //HasOwner = false; - - //return pk; + /// 切片得到新数据包,同时转移内存管理权,当前数据包应尽快停止使用 + /// + /// 可能是引用同一块内存,也可能是新的内存。 + /// 可能就是当前数据包,也可能引用相同的所有者或数组。 + /// + /// 偏移 + /// 个数。默认-1表示到末尾 + IPacket IPacket.Slice(Int32 offset, Int32 count) + { + if (count == 0) return Empty; IPacket? pk = null; var start = Offset + offset; @@ -645,8 +656,12 @@ public IPacket Slice(Int32 offset, Int32 count) pk = new ArrayPacket(_buffer, start, remain) { Next = Next.Slice(0, count - remain) }; } - if (pk is ArrayPacket ap) ap.HasOwner = HasOwner; - HasOwner = false; + // 所有权转移 + if (pk is ArrayPacket ap && ap._buffer == _buffer) + { + ap.HasOwner = HasOwner; + HasOwner = false; + } return pk; } diff --git a/NewLife.Core/Http/HttpBase.cs b/NewLife.Core/Http/HttpBase.cs index b70af12ba..181b61790 100644 --- a/NewLife.Core/Http/HttpBase.cs +++ b/NewLife.Core/Http/HttpBase.cs @@ -6,7 +6,7 @@ namespace NewLife.Http; /// Http请求响应基类 -public abstract class HttpBase +public abstract class HttpBase : IDisposable { #region 属性 /// 协议版本 @@ -36,6 +36,11 @@ public abstract class HttpBase public String this[String key] { get => Headers[key] + ""; set => Headers[key] = value; } #endregion + #region 构造 + /// 释放 + public void Dispose() => Body.TryDispose(); + #endregion + #region 解析 /// 快速验证协议头,剔除非HTTP协议。仅排除,验证通过不一定就是HTTP协议 /// @@ -50,7 +55,7 @@ public static Boolean FastValidHeader(ReadOnlySpan data) private static readonly Byte[] NewLine = [(Byte)'\r', (Byte)'\n']; private static readonly Byte[] NewLine2 = [(Byte)'\r', (Byte)'\n', (Byte)'\r', (Byte)'\n']; - /// 分析请求头 + /// 分析请求头。截取Body时获取缓冲区所有权 /// /// public Boolean Parse(IPacket pk) @@ -90,7 +95,7 @@ public Boolean Parse(IPacket pk) //var str = pk.ReadBytes(0, p).ToStr(); - // 截取 + // 截取主体,获取所有权 //var lines = str.Split("\r\n"); Body = pk.Slice(p + 4); @@ -118,14 +123,17 @@ public Boolean Parse(IPacket pk) #region 读写 /// 创建请求响应包 + /// 数据来自缓冲池,使用者用完返回数据包后应该释放,以便把缓冲区放回池里 /// - public virtual IPacket Build() + public virtual IOwnerPacket Build() { var body = Body; var len = body != null ? body.Total : 0; var header = BuildHeader(len); - var pk = new ArrayPacket(Encoding.UTF8.GetByteCount(header) + len); + + // 从内存池申请缓冲区,Slice后管理权转移,外部使用完以后释放 + using var pk = new ArrayPacket(Encoding.UTF8.GetByteCount(header) + len); var writer = new SpanWriter(pk.GetSpan()); //BuildHeader(writer, len); diff --git a/NewLife.Core/Http/HttpResponse.cs b/NewLife.Core/Http/HttpResponse.cs index 9799ca840..66a0841c7 100644 --- a/NewLife.Core/Http/HttpResponse.cs +++ b/NewLife.Core/Http/HttpResponse.cs @@ -43,7 +43,7 @@ protected override Boolean OnParse(String firstLine) /// 创建请求响应包 /// - public override IPacket Build() + public override IOwnerPacket Build() { // 如果响应异常,则使用响应描述作为内容 if (StatusCode > HttpStatusCode.OK && Body == null && !StatusDescription.IsNullOrEmpty()) diff --git a/NewLife.Core/Http/HttpSession.cs b/NewLife.Core/Http/HttpSession.cs index 2f904c0fe..48531a9d6 100644 --- a/NewLife.Core/Http/HttpSession.cs +++ b/NewLife.Core/Http/HttpSession.cs @@ -81,8 +81,10 @@ public void Process(IData data) if (req.ContentLength > MaxRequestLength) { var rs = new HttpResponse { StatusCode = HttpStatusCode.RequestEntityTooLarge }; - _session.Send(rs.Build()); + // 发送响应。用完后释放数据包,还给缓冲池 + using var res = rs.Build(); + _session.Send(res); _session.Dispose(); return; @@ -91,6 +93,10 @@ public void Process(IData data) _cache = new MemoryStream(req.ContentLength); req.Body?.CopyTo(_cache); //req.Body = req.Body.Clone(); + + // 请求主体数据来自缓冲区,要还回去 + req.Body.TryDispose(); + req.Body = null; } } else if (req != null) @@ -130,11 +136,20 @@ public void Process(IData data) var closing = !req.KeepAlive && _websocket == null; if (closing && !rs.Headers.ContainsKey("Connection")) rs.Headers["Connection"] = "close"; - _session.Send(rs.Build()); + // 发送响应。用完后释放数据包,还给缓冲池 + using var res = rs.Build(); + _session.Send(res); if (closing) _session.Dispose(); } } + + // 请求主体数据来自缓冲区,要还回去 + if (req != null) + { + req.Body.TryDispose(); + req.Body = null; + } } /// 收到新的Http请求,只有头部 diff --git a/NewLife.Core/Http/TinyHttpClient.cs b/NewLife.Core/Http/TinyHttpClient.cs index 1a5f12c00..b0c0236be 100644 --- a/NewLife.Core/Http/TinyHttpClient.cs +++ b/NewLife.Core/Http/TinyHttpClient.cs @@ -127,7 +127,7 @@ protected virtual async Task GetStreamAsync(Uri? uri) /// /// /// - protected virtual async Task SendDataAsync(Uri? uri, IPacket? request) + protected virtual async Task SendDataAsync(Uri? uri, IPacket? request) { var ns = await GetStreamAsync(uri).ConfigureAwait(false); @@ -135,7 +135,7 @@ protected virtual async Task SendDataAsync(Uri? uri, IPacket? request) if (request != null) await request.CopyToAsync(ns).ConfigureAwait(false); // 接收 - var pk = new ArrayPacket(BufferSize); + using var pk = new ArrayPacket(BufferSize); using var source = new CancellationTokenSource(Timeout); #if NETCOREAPP || NETSTANDARD2_1 @@ -162,11 +162,11 @@ protected virtual async Task SendDataAsync(Uri? uri, IPacket? request) while (retry-- > 0) { // 发出请求 - rs = await SendDataAsync(uri, req).ConfigureAwait(false); - if (rs == null || rs.Length == 0) return null; + using var rs2 = await SendDataAsync(uri, req).ConfigureAwait(false); + if (rs2 == null || rs2.Length == 0) return null; // 解析响应 - if (!res.Parse(rs)) return res; + if (!res.Parse(rs2)) return res; rs = res.Body; // 跳转 @@ -181,6 +181,8 @@ protected virtual async Task SendDataAsync(Uri? uri, IPacket? request) uri = uri2; request.RequestUri = uri; + + req.TryDispose(); req = request.Build(); continue; @@ -190,6 +192,9 @@ protected virtual async Task SendDataAsync(Uri? uri, IPacket? request) break; } + // 释放数据包,还给缓冲池 + req.TryDispose(); + if (res.StatusCode != HttpStatusCode.OK) throw new Exception($"{(Int32)res.StatusCode} {res.StatusDescription}"); // 如果没有收完数据包 @@ -199,6 +204,7 @@ protected virtual async Task SendDataAsync(Uri? uri, IPacket? request) var last = rs; while (total < res.ContentLength) { + //todo 这里的IPacket.Append可能有问题,因为last本质上是结构体 var pk = await SendDataAsync(null, null).ConfigureAwait(false); last.Append(pk); @@ -212,7 +218,10 @@ protected virtual async Task SendDataAsync(Uri? uri, IPacket? request) { // 如果不足则读取一个chunk,因为有可能第一个响应包只有头部 if (rs.Length == 0) + { + rs.TryDispose(); rs = await SendDataAsync(null, null).ConfigureAwait(false); + } res.Body = await ReadChunkAsync(rs); } @@ -285,6 +294,8 @@ protected virtual async Task ReadChunkAsync(IPacket body) if (pk == null || pk.Length == 0) break; if (pk.Length > 0) continue; + pk.TryDispose(); + // 读取新的数据片段,如果不存在则跳出 pk = await SendDataAsync(null, null).ConfigureAwait(false); if (pk == null || pk.Length == 0) break; diff --git a/NewLife.Core/Http/WebSocket.cs b/NewLife.Core/Http/WebSocket.cs index e0764b243..d3394c933 100644 --- a/NewLife.Core/Http/WebSocket.cs +++ b/NewLife.Core/Http/WebSocket.cs @@ -66,7 +66,7 @@ public class WebSocket /// public void Process(IPacket pk) { - var message = new WebSocketMessage(); + using var message = new WebSocketMessage(); if (message.Read(pk)) { ActiveTime = DateTime.Now; @@ -110,7 +110,7 @@ private void Send(WebSocketMessage msg) var socket = Context?.Socket; if (session == null && socket == null) throw new ObjectDisposedException(nameof(Context)); - var data = msg.ToPacket(); + using var data = msg.ToPacket(); if (session != null) session.Send(data); else @@ -138,7 +138,7 @@ public void SendAll(IPacket data, WebSocketMessageType type, FuncWebSocket消息 -public class WebSocketMessage +public class WebSocketMessage : IDisposable { #region 属性 /// 消息是否结束 @@ -50,6 +50,11 @@ public class WebSocketMessage public String? StatusDescription { get; set; } #endregion + #region 构造 + /// 销毁。回收数据包到内存池 + public void Dispose() => Payload.TryDispose(); + #endregion + #region 方法 /// 读取消息 /// @@ -123,7 +128,7 @@ public Boolean Read(IPacket pk) /// 把消息转为封包 /// - public virtual IPacket ToPacket() + public virtual IOwnerPacket ToPacket() { var pk = Payload; var len = pk == null ? 0 : pk.Length; @@ -207,7 +212,7 @@ public virtual IPacket ToPacket() if (!StatusDescription.IsNullOrEmpty()) writer.Write(StatusDescription); } - return rs.Slice(0, writer.Position); + return (rs.Slice(0, writer.Position) as IOwnerPacket)!; } #endregion } \ No newline at end of file diff --git a/NewLife.Core/Net/Handlers/LengthFieldCodec.cs b/NewLife.Core/Net/Handlers/LengthFieldCodec.cs index a54ce6768..4b30d04d1 100644 --- a/NewLife.Core/Net/Handlers/LengthFieldCodec.cs +++ b/NewLife.Core/Net/Handlers/LengthFieldCodec.cs @@ -34,7 +34,8 @@ protected override Object Encode(IHandlerContext context, IPacket msg) // 尝试退格,直接利用缓冲区 if (msg is ArrayPacket ap && ap.Offset >= len) { - msg = new ArrayPacket(ap.Buffer, ap.Offset - len, ap.Length + len) { Next = ap.Next }; + // 向下传递时,不要转移所有权。向上传递到较高层级才需要转移所有权。 + msg = new ArrayPacket(ap.Buffer, ap.Offset - len, ap.Length + len) { Next = ap.Next/*, HasOwner = ap.HasOwner*/ }; } else { diff --git a/NewLife.Core/Net/Handlers/WebSocketCodec.cs b/NewLife.Core/Net/Handlers/WebSocketCodec.cs index 5f7551a10..2b2d9c4a7 100644 --- a/NewLife.Core/Net/Handlers/WebSocketCodec.cs +++ b/NewLife.Core/Net/Handlers/WebSocketCodec.cs @@ -56,7 +56,15 @@ public override Boolean Close(IHandlerContext context, String reason) } } - return base.Read(context, message); + try + { + return base.Read(context, message); + } + finally + { + // 下游可能忘了释放内存,这里兜底释放 + message.TryDispose(); + } } /// 发送消息时,写入数据 @@ -71,6 +79,14 @@ public override Boolean Close(IHandlerContext context, String reason) if (message is WebSocketMessage msg) message = msg.ToPacket(); - return base.Write(context, message); + try + { + return base.Write(context, message); + } + finally + { + // 下游可能忘了释放内存,这里兜底释放 + message.TryDispose(); + } } } diff --git a/NewLife.Core/Net/ISocketRemote.cs b/NewLife.Core/Net/ISocketRemote.cs index f7165465d..bdb7f9e4b 100644 --- a/NewLife.Core/Net/ISocketRemote.cs +++ b/NewLife.Core/Net/ISocketRemote.cs @@ -32,11 +32,11 @@ public interface ISocketRemote : ISocket, IExtend #region 接收 /// 接收数据。阻塞当前线程等待返回 /// - IPacket? Receive(); + IOwnerPacket? Receive(); /// 异步接收数据 /// - Task ReceiveAsync(CancellationToken cancellationToken = default); + Task ReceiveAsync(CancellationToken cancellationToken = default); /// 数据到达事件 event EventHandler Received; @@ -133,7 +133,7 @@ public static Int32 Send(this ISocketRemote session, String msg, Encoding? encod /// public static String ReceiveString(this ISocketRemote session, Encoding? encoding = null) { - var pk = session.Receive(); + using var pk = session.Receive(); if (pk == null || pk.Length == 0) return String.Empty; return pk.ToStr(encoding ?? Encoding.UTF8); diff --git a/NewLife.Core/Net/ITransport.cs b/NewLife.Core/Net/ITransport.cs index ce31caa13..bdbb93722 100644 --- a/NewLife.Core/Net/ITransport.cs +++ b/NewLife.Core/Net/ITransport.cs @@ -21,7 +21,7 @@ public interface ITransport : IDisposable /// 读取数据 /// - IPacket? Receive(); + IOwnerPacket? Receive(); /// 数据到达事件 event EventHandler Received; diff --git a/NewLife.Core/Net/SessionBase.cs b/NewLife.Core/Net/SessionBase.cs index 1601e5ac9..f5a3ad14d 100644 --- a/NewLife.Core/Net/SessionBase.cs +++ b/NewLife.Core/Net/SessionBase.cs @@ -262,7 +262,7 @@ public Int32 Send(IPacket data) /// 接收数据 /// - public virtual IPacket? Receive() + public virtual IOwnerPacket? Receive() { if (Disposed) throw new ObjectDisposedException(GetType().Name); @@ -271,7 +271,7 @@ public Int32 Send(IPacket data) using var span = Tracer?.NewSpan($"net:{Name}:Receive", BufferSize + ""); try { - var pk = new ArrayPacket(BufferSize); + using var pk = new ArrayPacket(BufferSize); var size = Client.Receive(pk.Buffer, SocketFlags.None); if (span != null) span.Value = size; @@ -286,7 +286,7 @@ public Int32 Send(IPacket data) /// 异步接收数据 /// - public virtual async Task ReceiveAsync(CancellationToken cancellationToken = default) + public virtual async Task ReceiveAsync(CancellationToken cancellationToken = default) { if (Disposed) throw new ObjectDisposedException(GetType().Name); @@ -295,7 +295,7 @@ public Int32 Send(IPacket data) using var span = Tracer?.NewSpan($"net:{Name}:ReceiveAsync", BufferSize + ""); try { - var pk = new ArrayPacket(BufferSize); + using var pk = new ArrayPacket(BufferSize); #if NETFRAMEWORK || NETSTANDARD2_0 var ar = Client.BeginReceive(pk.Buffer, 0, pk.Length, SocketFlags.None, null, Client); var size = ar.IsCompleted ? diff --git a/NewLife.Core/Net/TcpSession.cs b/NewLife.Core/Net/TcpSession.cs index 6362c8c58..1f78ed5bf 100644 --- a/NewLife.Core/Net/TcpSession.cs +++ b/NewLife.Core/Net/TcpSession.cs @@ -378,7 +378,7 @@ protected override Int32 OnSend(IPacket pk) #region 接收 /// 异步接收数据。重载以支持SSL /// - public override async Task ReceiveAsync(CancellationToken cancellationToken = default) + public override async Task ReceiveAsync(CancellationToken cancellationToken = default) { if (!Open() || Client == null) return null; @@ -388,7 +388,7 @@ protected override Int32 OnSend(IPacket pk) using var span = Tracer?.NewSpan($"net:{Name}:ReceiveAsync", BufferSize + ""); try { - var pk = new ArrayPacket(BufferSize); + using var pk = new ArrayPacket(BufferSize); var size = await ss.ReadAsync(pk.Buffer, 0, pk.Length, cancellationToken); if (span != null) span.Value = size; diff --git a/NewLife.Core/Net/UdpSession.cs b/NewLife.Core/Net/UdpSession.cs index 33b84a3a8..fb78ae137 100644 --- a/NewLife.Core/Net/UdpSession.cs +++ b/NewLife.Core/Net/UdpSession.cs @@ -247,7 +247,7 @@ void TrySetCanceled(Object? state) #region 接收 /// 接收数据 /// - public IPacket Receive() + public IOwnerPacket Receive() { if (Disposed) throw new ObjectDisposedException(GetType().Name); if (Server?.Client == null) throw new InvalidOperationException(nameof(Server)); @@ -256,7 +256,7 @@ public IPacket Receive() try { var ep = Remote.EndPoint as EndPoint; - var pk = new ArrayPacket(Server.BufferSize); + using var pk = new ArrayPacket(Server.BufferSize); var size = Server.Client.ReceiveFrom(pk.Buffer, ref ep); if (span != null) span.Value = size; @@ -271,7 +271,7 @@ public IPacket Receive() /// 异步接收数据 /// - public virtual async Task ReceiveAsync(CancellationToken cancellationToken = default) + public virtual async Task ReceiveAsync(CancellationToken cancellationToken = default) { if (Disposed) throw new ObjectDisposedException(GetType().Name); if (Server?.Client == null) throw new InvalidOperationException(nameof(Server)); @@ -280,7 +280,7 @@ public IPacket Receive() try { var ep = Remote.EndPoint as EndPoint; - var pk = new ArrayPacket(Server.BufferSize); + using var pk = new ArrayPacket(Server.BufferSize); var socket = Server.Client; #if NETFRAMEWORK || NETSTANDARD2_0 var ar = socket.BeginReceiveFrom(pk.Buffer, 0, pk.Length, SocketFlags.None, ref ep, null, socket); diff --git a/NewLife.Core/Net/WebSocketClient.cs b/NewLife.Core/Net/WebSocketClient.cs index 9eab522f4..446b145c5 100644 --- a/NewLife.Core/Net/WebSocketClient.cs +++ b/NewLife.Core/Net/WebSocketClient.cs @@ -105,7 +105,7 @@ public void SetRequestHeader(String headerName, String? headerValue) /// public virtual async Task ReceiveMessageAsync(CancellationToken cancellationToken = default) { - var rs = await base.ReceiveAsync(cancellationToken); + using var rs = await base.ReceiveAsync(cancellationToken); if (rs == null) return null; var msg = new WebSocketMessage(); @@ -233,16 +233,18 @@ public static Boolean Handshake(ISocketClient client, Uri uri) using var span = client.Tracer?.NewSpan($"net:{client.Name}:WebSocket", uri + ""); try { - // 发送请求 - var req = request.Build(); - client.Send(req); + // 发送请求。用完后释放数据包,还给缓冲池 + { + using var req = request.Build(); + client.Send(req); + } // 接收响应 - var rs = client.Receive(); + using var rs = client.Receive(); if (rs == null || rs.Length == 0) return false; // 解析响应 - var res = new HttpResponse(); + using var res = new HttpResponse(); if (!res.Parse(rs)) return false; //if (res.StatusCode != HttpStatusCode.OK) throw new Exception($"{(Int32)res.StatusCode} {res.StatusDescription}"); diff --git a/NewLife.Core/Serialization/Binary/Binary.cs b/NewLife.Core/Serialization/Binary/Binary.cs index 0e2753f5f..1729baaf4 100644 --- a/NewLife.Core/Serialization/Binary/Binary.cs +++ b/NewLife.Core/Serialization/Binary/Binary.cs @@ -544,6 +544,8 @@ public static IPacket FastWrite(Object value, Boolean encodeInt = true) //return new ArrayPacket(buf, 8, buf.Length - 8); bn.Stream.Position = 8; + + // 包装为数据包,直接窃取内存流内部的缓冲区 return new ArrayPacket(bn.Stream); } diff --git a/NewLife.Core/Serialization/Interface/IAccessor.cs b/NewLife.Core/Serialization/Interface/IAccessor.cs index b4d21b8a6..fbab08fb0 100644 --- a/NewLife.Core/Serialization/Interface/IAccessor.cs +++ b/NewLife.Core/Serialization/Interface/IAccessor.cs @@ -44,10 +44,12 @@ public static class AccessorHelper /// public static IPacket ToPacket(this IAccessor accessor, Object? context = null) { - var ms = new MemoryStream(); + var ms = new MemoryStream { Position = 8 }; accessor.Write(ms, context); - ms.Position = 0; + ms.Position = 8; + + // 包装为数据包,直接窃取内存流内部的缓冲区 return new ArrayPacket(ms); } diff --git a/NewLife.Core/Serialization/Interface/IFormatterX.cs b/NewLife.Core/Serialization/Interface/IFormatterX.cs index 001a149c8..aa7cad19e 100644 --- a/NewLife.Core/Serialization/Interface/IFormatterX.cs +++ b/NewLife.Core/Serialization/Interface/IFormatterX.cs @@ -130,6 +130,8 @@ public Byte[] GetBytes() public IPacket GetPacket() { Stream.Position = 0; + + // 包装为数据包,直接窃取内存流内部的缓冲区 return new ArrayPacket(Stream); } #endregion diff --git a/Samples/Zero.HttpServer/ClientTest.cs b/Samples/Zero.HttpServer/ClientTest.cs index 980c4b3ce..4e2788974 100644 --- a/Samples/Zero.HttpServer/ClientTest.cs +++ b/Samples/Zero.HttpServer/ClientTest.cs @@ -77,7 +77,7 @@ public static async Task WebSocketClientTest() await client.SendTextAsync("Hello NewLife"); - var rs = await client.ReceiveMessageAsync(default); + using var rs = await client.ReceiveMessageAsync(default); client.WriteLog(rs.Payload.ToStr()); await Task.Delay(6_000); @@ -86,8 +86,8 @@ public static async Task WebSocketClientTest() await client.CloseAsync(1000, "通信完成", default); client.WriteLog("Close"); - rs = await client.ReceiveMessageAsync(default); - client.WriteLog("Close [{0}] {1}", rs.CloseStatus, rs.StatusDescription); + using var rs2 = await client.ReceiveMessageAsync(default); + client.WriteLog("Close [{0}] {1}", rs2.CloseStatus, rs2.StatusDescription); client.Dispose(); } diff --git a/Samples/Zero.Server/ClientTest.cs b/Samples/Zero.Server/ClientTest.cs index a99732111..b3d375fc0 100644 --- a/Samples/Zero.Server/ClientTest.cs +++ b/Samples/Zero.Server/ClientTest.cs @@ -87,7 +87,7 @@ public static async void TcpSessionTest() if (client is TcpSession tcp) tcp.MaxAsync = 0; // 接收服务端握手。内部自动建立连接 - var rs = await client.ReceiveAsync(default); + using var rs = await client.ReceiveAsync(default); client.WriteLog("<={0}", rs.ToStr()); // 发送数据 @@ -96,8 +96,8 @@ public static async void TcpSessionTest() client.Send(str); // 接收数据 - rs = await client.ReceiveAsync(default); - client.WriteLog("<={0}", rs.ToStr()); + using var rs2 = await client.ReceiveAsync(default); + client.WriteLog("<={0}", rs2.ToStr()); // 关闭连接 client.Close("测试完成"); @@ -123,12 +123,12 @@ public static async void UdpSessionTest() client.Send(str); // 接收服务端握手 - var rs = await client.ReceiveAsync(default); + using var rs = await client.ReceiveAsync(default); client.WriteLog("<={0}", rs.ToStr()); // 接收数据 - rs = await client.ReceiveAsync(default); - client.WriteLog("<={0}", rs.ToStr()); + using var rs2 = await client.ReceiveAsync(default); + client.WriteLog("<={0}", rs2.ToStr()); // 关闭连接 client.Close("测试完成");