diff --git a/NewLife.Core/Data/IPacket.cs b/NewLife.Core/Data/IPacket.cs
index b7a1fe05a..fb98e6bbf 100644
--- a/NewLife.Core/Data/IPacket.cs
+++ b/NewLife.Core/Data/IPacket.cs
@@ -381,14 +381,14 @@ protected override void Dispose(Boolean disposing)
///
public Memory GetMemory() => new(_buffer, _offset, _length);
- /// 切片得到新数据包,同时转移内存管理权,当前数据包应尽快停止使用
- ///
- /// 可能是引用同一块内存,也可能是新的内存。
- /// 可能就是当前数据包,也可能引用相同的所有者或数组。
- ///
- /// 偏移
- /// 个数。默认-1表示到末尾
- IPacket IPacket.Slice(Int32 offset, Int32 count) => Slice(offset, count);
+ ///// 切片得到新数据包,同时转移内存管理权,当前数据包应尽快停止使用
+ /////
+ ///// 可能是引用同一块内存,也可能是新的内存。
+ ///// 可能就是当前数据包,也可能引用相同的所有者或数组。
+ /////
+ ///// 偏移
+ ///// 个数。默认-1表示到末尾
+ //IPacket IPacket.Slice(Int32 offset, Int32 count) => Slice(offset, count);
/// 切片得到新数据包,同时转移内存管理权,当前数据包应尽快停止使用
///
@@ -397,7 +397,7 @@ protected override void Dispose(Boolean disposing)
///
/// 偏移
/// 个数。默认-1表示到末尾
- public OwnerPacket Slice(Int32 offset, Int32 count)
+ public OwnerPacket SliceSingle(Int32 offset, Int32 count)
{
// 带有Next时,不支持Slice
if (Next != null) throw new NotSupportedException("Slice with Next");
@@ -415,6 +415,72 @@ public OwnerPacket Slice(Int32 offset, Int32 count)
return new OwnerPacket(buffer, _offset + offset, count);
}
+ /// 切片得到新数据包,同时转移内存管理权,当前数据包应尽快停止使用
+ ///
+ /// 可能是引用同一块内存,也可能是新的内存。
+ /// 可能就是当前数据包,也可能引用相同的所有者或数组。
+ ///
+ /// 偏移
+ /// 个数。默认-1表示到末尾
+ public IPacket Slice(Int32 offset, Int32 count)
+ {
+ //// 带有Next时,不支持Slice
+ //if (Next != null) throw new NotSupportedException("Slice with Next");
+
+ // 只有一次Slice机会
+ if (_buffer == null) throw new InvalidDataException();
+
+ var start = _offset + offset;
+ var remain = _length - offset;
+
+ // 超出范围
+ if (count > Total - offset) throw new ArgumentOutOfRangeException(nameof(count), "count must be non-negative and less than or equal to the memory owner's length.");
+
+ // 单个数据包
+ if (Next == null)
+ {
+ // 转移管理权
+ var buffer = _buffer;
+ _buffer = null!;
+
+ if (count < 0 || count > remain) count = remain;
+ return new OwnerPacket(buffer, start, count);
+ }
+ else
+ {
+ // 如果当前段用完,则取下一段
+ if (remain <= 0)
+ {
+ var rs = Next.Slice(offset - _length, count);
+ // 切分后数据包要用到Next,这里清空避免当前包释放时把Next也释放
+ Next = null;
+ return rs;
+ }
+
+ // 转移管理权
+ var buffer = _buffer;
+ _buffer = null!;
+
+ // 当前包用一截,剩下的全部
+ if (count < 0)
+ {
+ var rs = new OwnerPacket(buffer, start, remain) { Next = Next };
+ Next = null;
+ return rs;
+ }
+
+ // 当前包可以读完
+ if (count <= remain) return new OwnerPacket(buffer, start, count);
+
+ // 当前包用一截,剩下的再截取
+ {
+ var rs = new OwnerPacket(buffer, start, remain) { Next = Next.Slice(0, count - remain) };
+ Next = null;
+ return rs;
+ }
+ }
+ }
+
/// 尝试获取数据段
///
///
diff --git a/NewLife.Core/Http/HttpBase.cs b/NewLife.Core/Http/HttpBase.cs
index 7ed70c01d..123f9cfbc 100644
--- a/NewLife.Core/Http/HttpBase.cs
+++ b/NewLife.Core/Http/HttpBase.cs
@@ -143,7 +143,7 @@ public virtual IOwnerPacket Build()
if (body != null) writer.Write(body.GetSpan());
- return pk.Slice(0, writer.Position);
+ return pk.SliceSingle(0, writer.Position);
}
/// 创建头部
diff --git a/NewLife.Core/Http/TinyHttpClient.cs b/NewLife.Core/Http/TinyHttpClient.cs
index b07e13959..9a83ec8e5 100644
--- a/NewLife.Core/Http/TinyHttpClient.cs
+++ b/NewLife.Core/Http/TinyHttpClient.cs
@@ -144,7 +144,7 @@ protected virtual async Task SendDataAsync(Uri? uri, IPacket? requ
var count = await ns.ReadAsync(pk.Buffer, 0, pk.Length, source.Token).ConfigureAwait(false);
#endif
- return pk.Slice(0, count);
+ return pk.SliceSingle(0, count);
}
/// 异步发出请求,并接收响应
diff --git a/NewLife.Core/Http/WebSocket.cs b/NewLife.Core/Http/WebSocket.cs
index f569e066b..17c82d6a9 100644
--- a/NewLife.Core/Http/WebSocket.cs
+++ b/NewLife.Core/Http/WebSocket.cs
@@ -110,11 +110,12 @@ private void Send(WebSocketMessage msg)
var socket = Context?.Socket;
if (session == null && socket == null) throw new ObjectDisposedException(nameof(Context));
- using var data = msg.ToPacket();
+ var data = msg.ToPacket();
if (session != null)
session.Send(data);
else
socket?.Send(data);
+ data.TryDispose();
}
/// 发送消息
@@ -138,8 +139,9 @@ public void SendAll(IPacket data, WebSocketMessageType type, Func想所有连接发送文本消息
diff --git a/NewLife.Core/Http/WebSocketMessage.cs b/NewLife.Core/Http/WebSocketMessage.cs
index 3476d669c..aada111e1 100644
--- a/NewLife.Core/Http/WebSocketMessage.cs
+++ b/NewLife.Core/Http/WebSocketMessage.cs
@@ -128,10 +128,10 @@ public Boolean Read(IPacket pk)
/// 把消息转为封包
///
- public virtual IOwnerPacket ToPacket()
+ public virtual IPacket ToPacket()
{
- var pk = Payload;
- var len = pk == null ? 0 : pk.Total;
+ var body = Payload;
+ var len = body == null ? 0 : body.Total;
// 特殊处理关闭消息
if (len == 0 && Type == WebSocketMessageType.Close)
@@ -194,9 +194,9 @@ public virtual IOwnerPacket ToPacket()
writer.Write(masks);
// 掩码混淆数据。直接在数据缓冲区修改,避免拷贝
- if (Payload != null)
+ if (body != null)
{
- var data = Payload.GetSpan();
+ var data = body.GetSpan();
for (var i = 0; i < len; i++)
{
data[i] = (Byte)(data[i] ^ masks[i % 4]);
@@ -204,8 +204,12 @@ public virtual IOwnerPacket ToPacket()
}
}
- if (pk != null && pk.Length > 0)
- writer.Write(pk.GetSpan());
+ if (body != null && body.Length > 0)
+ {
+ // 注意body可能是链式数据包
+ //writer.Write(body.GetSpan());
+ return rs.Slice(0, writer.Position).Append(body);
+ }
else if (Type == WebSocketMessageType.Close)
{
writer.Write((Int16)CloseStatus);
diff --git a/NewLife.Core/Net/Handlers/MessageCodec.cs b/NewLife.Core/Net/Handlers/MessageCodec.cs
index 709abb968..49bab7c72 100644
--- a/NewLife.Core/Net/Handlers/MessageCodec.cs
+++ b/NewLife.Core/Net/Handlers/MessageCodec.cs
@@ -48,14 +48,14 @@ public class MessageCodec : Handler
public override Object? Write(IHandlerContext context, Object message)
{
// 谁申请,谁归还
- IOwnerPacket? owner = null;
+ IPacket? owner = null;
if (message is T msg)
{
var rs = Encode(context, msg);
if (rs == null) return null;
message = rs;
- owner = rs as IOwnerPacket;
+ owner = rs as IPacket;
// 加入队列,忽略请求消息
if (message is IMessage msg2)
@@ -73,7 +73,7 @@ public class MessageCodec : Handler
finally
{
// 下游可能忘了释放内存,这里兜底释放
- owner?.Dispose();
+ owner.TryDispose();
}
}
diff --git a/NewLife.Core/Net/Handlers/WebSocketCodec.cs b/NewLife.Core/Net/Handlers/WebSocketCodec.cs
index 70c13b6d5..8841f9078 100644
--- a/NewLife.Core/Net/Handlers/WebSocketCodec.cs
+++ b/NewLife.Core/Net/Handlers/WebSocketCodec.cs
@@ -69,7 +69,7 @@ public override Boolean Close(IHandlerContext context, String reason)
message = new WebSocketMessage { Type = WebSocketMessageType.Binary, Payload = pk };
// 谁申请,谁归还
- IOwnerPacket? owner = null;
+ IPacket? owner = null;
if (message is WebSocketMessage msg)
message = owner = msg.ToPacket();
@@ -80,7 +80,7 @@ public override Boolean Close(IHandlerContext context, String reason)
finally
{
// 下游可能忘了释放内存,这里兜底释放
- owner?.Dispose();
+ owner.TryDispose();
}
}
}
diff --git a/NewLife.Core/Net/SessionBase.cs b/NewLife.Core/Net/SessionBase.cs
index 51ff44316..cdc071b57 100644
--- a/NewLife.Core/Net/SessionBase.cs
+++ b/NewLife.Core/Net/SessionBase.cs
@@ -275,7 +275,7 @@ public Int32 Send(IPacket data)
var size = Client.Receive(pk.Buffer, SocketFlags.None);
if (span != null) span.Value = size;
- return pk.Slice(0, size);
+ return pk.SliceSingle(0, size);
}
catch (Exception ex)
{
@@ -306,7 +306,7 @@ public Int32 Send(IPacket data)
#endif
if (span != null) span.Value = size;
- return pk.Slice(0, size);
+ return pk.SliceSingle(0, size);
}
catch (Exception ex)
{
diff --git a/NewLife.Core/Net/TcpSession.cs b/NewLife.Core/Net/TcpSession.cs
index ab1ca1628..fed7c4fd3 100644
--- a/NewLife.Core/Net/TcpSession.cs
+++ b/NewLife.Core/Net/TcpSession.cs
@@ -390,7 +390,7 @@ protected override Int32 OnSend(IPacket pk)
var size = await ss.ReadAsync(pk.Buffer, 0, pk.Length, cancellationToken);
if (span != null) span.Value = size;
- return pk.Slice(0, size);
+ return pk.SliceSingle(0, size);
}
catch (Exception ex)
{
diff --git a/NewLife.Core/Net/UdpSession.cs b/NewLife.Core/Net/UdpSession.cs
index 0d0f01014..c1d984446 100644
--- a/NewLife.Core/Net/UdpSession.cs
+++ b/NewLife.Core/Net/UdpSession.cs
@@ -253,7 +253,7 @@ public IOwnerPacket Receive()
var size = Server.Client.ReceiveFrom(pk.Buffer, ref ep);
if (span != null) span.Value = size;
- return pk.Slice(0, size);
+ return pk.SliceSingle(0, size);
}
catch (Exception ex)
{
@@ -289,7 +289,7 @@ public IOwnerPacket Receive()
#endif
if (span != null) span.Value = size;
- return pk.Slice(0, size);
+ return pk.SliceSingle(0, size);
}
catch (Exception ex)
{
diff --git a/XUnitTest.Core/Http/WebSocketMessageTests.cs b/XUnitTest.Core/Http/WebSocketMessageTests.cs
new file mode 100644
index 000000000..512631218
--- /dev/null
+++ b/XUnitTest.Core/Http/WebSocketMessageTests.cs
@@ -0,0 +1,107 @@
+using System;
+using NewLife.Data;
+using NewLife.Http;
+using NewLife.Messaging;
+using Xunit;
+
+namespace XUnitTest.Http;
+
+public class WebSocketMessageTests
+{
+ [Fact]
+ public void Text()
+ {
+ var msg = new WebSocketMessage
+ {
+ Type = WebSocketMessageType.Text,
+ Payload = (ArrayPacket)$"Hello NewLife",
+ };
+
+ var pk = msg.ToPacket();
+ Assert.Equal("810D48656C6C6F204E65774C696665", pk.ToHex());
+
+ var msg2 = new WebSocketMessage();
+ var rs = msg2.Read(pk);
+ Assert.True(rs);
+
+ Assert.Equal(msg.Type, msg2.Type);
+ Assert.Equal(msg.Payload.ToHex(), msg2.Payload.ToHex());
+ }
+
+ [Fact]
+ public void Ping()
+ {
+ var msg = new WebSocketMessage
+ {
+ Type = WebSocketMessageType.Ping,
+ Payload = (ArrayPacket)$"Ping {DateTime.UtcNow.ToFullString()}",
+ };
+
+ var pk = msg.ToPacket();
+ Assert.StartsWith("891850696E67", pk.ToHex());
+
+ var msg2 = new WebSocketMessage();
+ var rs = msg2.Read(pk);
+ Assert.True(rs);
+
+ Assert.Equal(msg.Type, msg2.Type);
+ Assert.Equal(msg.Payload.ToHex(), msg2.Payload.ToHex());
+ }
+
+ [Fact]
+ public void Close()
+ {
+ var msg = new WebSocketMessage
+ {
+ Type = WebSocketMessageType.Close,
+ CloseStatus = 1000,
+ StatusDescription = "Finish",
+ };
+
+ var pk = msg.ToPacket();
+ Assert.Equal("880803E846696E697368", pk.ToHex());
+
+ var msg2 = new WebSocketMessage();
+ var rs = msg2.Read(pk);
+ Assert.True(rs);
+
+ Assert.Equal(msg.Type, msg2.Type);
+ Assert.Equal(msg.CloseStatus, msg2.CloseStatus);
+ Assert.Equal(msg.StatusDescription, msg2.StatusDescription);
+ }
+
+ [Fact]
+ public void DefaultMessageOverWebsocket()
+ {
+ var dm = new DefaultMessage
+ {
+ Flag = 0x01,
+ Sequence = 0xAB,
+ Payload = (ArrayPacket)"Hello NewLife"
+ };
+
+ var msg = new WebSocketMessage
+ {
+ Type = WebSocketMessageType.Binary,
+ Payload = dm.ToPacket(),
+ };
+
+ var pk = msg.ToPacket();
+ Assert.Equal("821101AB0D0048656C6C6F204E65774C696665", pk.ToHex());
+
+ var msg2 = new WebSocketMessage();
+ var rs = msg2.Read(pk);
+ Assert.True(rs);
+
+ Assert.Equal(msg.Type, msg2.Type);
+ Assert.Equal(msg.Payload.ToHex(), msg2.Payload.ToHex());
+
+ var dm2 = new DefaultMessage();
+ rs = dm2.Read(msg2.Payload);
+ Assert.True(rs);
+
+ Assert.Equal(dm.Flag, dm2.Flag);
+ Assert.Equal(dm.Sequence, dm2.Sequence);
+ Assert.Equal(dm.Payload.ToHex(), dm2.Payload.ToHex());
+ }
+}
\ No newline at end of file