Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
@page "/socket/data-entity"
@inject IStringLocalizer<DataEntities> Localizer

<h3>@Localizer["DataEntityTitle"]</h3>
<h4>@Localizer["DataEntityDescription"]</h4>

<DemoBlock Title="@Localizer["NormalTitle"]"
Introduction="@Localizer["NormalIntro"]"
Name="Normal" ShowCode="false">
<p>通过 <code>SocketDataConverterAttribute</code> 类标签与 <code>SocketDataPropertyAttribute</code>
属性标签可以将通讯数据自动转化为我们系统中需要的业务实体类,示例如下:</p>
<Pre>[SocketDataConverter(Type = typeof(MockEntitySocketDataConverter))]
class MockEntity
{
[SocketDataProperty(Type = typeof(byte[]), Offset = 0, Length = 5)]
public byte[]? Header { get; set; }

[SocketDataProperty(Type = typeof(byte[]), Offset = 5, Length = 2)]
public byte[]? Body { get; set; }

[SocketDataProperty(Type = typeof(string), Offset = 7, Length = 1, EncodingName = "utf-8")]
public string? Value1 { get; set; }

[SocketDataProperty(Type = typeof(int), Offset = 8, Length = 1)]
public int Value2 { get; set; }
}</Pre>
<p class="code-label">1. <code>SocketDataConverter</code> 参数说明:</p>
<ul class="ul-demo">
<li><b>Type</b>: 自定义转换器类型,组件库内置了 <code>SocketDataConverterBase</code> 泛型基类,继承后非常方便扩展出自己的转换器,重载
<code>TryConvertTo</code> 方法调用基类的 <code>Parse</code> 基本就完成了 99% 的工作量,也可以完全自己解析通讯电文到实体类的各个属性上
</li>
</ul>
<Pre>class MockEntitySocketDataConverter : SocketDataConverterBase&lt;MockEntity&gt;
{
public override bool TryConvertTo(ReadOnlyMemory&lt;byte&gt; data, [NotNullWhen(true)] out MockEntity? entity)
{
var v = new MockEntity();

// 调用基类的 Parse 方法即可
var ret = Parse(data, v);
entity = ret ? v : null;
return ret;
}
}</Pre>
<p><code>Parse</code> 方法也是可以重载自定义实现的,内置实现逻辑如下:</p>
<Pre>protected virtual bool Parse(ReadOnlyMemory&lt;byte&gt; data, TEntity entity)
{
// 使用 SocketDataPropertyAttribute 特性获取数据转换规则
var ret = false;
if (entity != null)
{
var properties = entity.GetType().GetProperties().Where(p => p.CanWrite).ToList();
foreach (var p in properties)
{
var attr = p.GetCustomAttribute&lt;SocketDataPropertyAttribute&gt;(false);
if (attr != null)
{
p.SetValue(entity, attr.ConvertTo(data));
}
}
ret = true;
}
return ret;
}</Pre>
<p>即遍历所有有 <code>SocketDataPropertyAttribute</code> 标签的属性对其进行过自动赋值</p>
<p class="code-label">2. <code>SocketDataPropertyAttribute</code> 参数说明</p>
<ul class="ul-demo">
<li><b>Type</b>: 转换目标数据类型</li>
<li><b>Offset</b>: 数据偏移量,即在接收到的数据中起始位置</li>
<li><b>Length</b>: 数据长度,即在接收到的数据中占的长度</li>
<li><b>EncodingName</b>: 转成字符串时需要的编码名称,默认 null 使用 <code>utf-8</code> 编码</li>
<li><b>ConverterType</b>: 自定义转化器类型,继承 <code>ISocketDataPropertyConverter</code> 接口</li>
<li><b>ConverterParameters</b>: 自定义转化器类型构造函数所需的参数,默认 null</li>
</ul>
<p>组件库内置了大量数据类型转换器</p>
<ul class="ul-demo">
<li><code>SocketDataByteArrayConverter</code> 转成 byte[] 数组类型</li>
<li><code>SocketDataStringConverter</code> 转成 string 字符串类型</li>
<li><code>SocketDataEnumConverter</code> 转成 enum 枚举类型</li>
<li><code>SocketDataBoolConverter</code> 转成 bool 布尔类型</li>
<li><code>SocketDataInt16BigEndianConverter</code> 转成 short 整形大端读取</li>
<li><code>SocketDataInt32BigEndianConverter</code> 转成 int 整形大端读取</li>
<li><code>SocketDataInt64BigEndianConverter</code> 转成 long 长整形大端读取</li>
<li><code>SocketDataSingleBigEndianConverter</code> 转成 float 单精度浮点数大端读取</li>
<li><code>SocketDataDoubleBigEndianConverter</code> 转成 double 双精度浮点数大端读取</li>
<li><code>SocketDataUInt16BigEndianConverter</code> 转成 ushort 无符号整形大端读取</li>
<li><code>SocketDataUInt32BigEndianConverter</code> 转成 uint 无符号整形大端读取</li>
<li><code>SocketDataUInt64BigEndianConverter</code> 转成 ulong 无符号长整形大端读取</li>
<li><code>SocketDataInt16LittleEndianConverter</code> 转成 short 整形小端读取</li>
<li><code>SocketDataInt32LittleEndianConverter</code> 转成 int 整形小端读取</li>
<li><code>SocketDataInt64LittleEndianConverter</code> 转成 long 长整形小端读取</li>
<li><code>SocketDataSingleLittleEndianConverter</code> 转成 float 单精度浮点数小端读取</li>
<li><code>SocketDataDoubleLittleEndianConverter</code> 转成 double 双精度浮点数小端读取</li>
<li><code>SocketDataUInt16LittleEndianConverter</code> 转成 ushort 无符号整形小端读取</li>
<li><code>SocketDataUInt32LittleEndianConverter</code> 转成 uint 无符号整形小端读取</li>
<li><code>SocketDataUInt64LittleEndianConverter</code> 转成 ulong 无符号整形小端读取</li>
</ul>
<p>自定义数据类型转化器示例</p>
<Pre>[SocketDataConverter(Type = typeof(MockEntitySocketDataConverter))]
class MockEntity
{
[SocketDataProperty(Type = typeof(byte[]), Offset = 0, Length = 5)]
public byte[]? Header { get; set; }

[SocketDataProperty(Type = typeof(byte[]), Offset = 5, Length = 2)]
public byte[]? Body { get; set; }

[SocketDataProperty(Type = typeof(Foo), Offset = 7, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])]
public string? Value1 { get; set; }
}

class FooConverter(string name) : ISocketDataPropertyConverter
{
public object? Convert(ReadOnlyMemory&lt;byte&gt; data)
{
return new Foo() { Id = data.Span[0], Name = name };
}
}</Pre>
<p>更多技术细节可以参考以上内置提供的转换器源码</p>
</DemoBlock>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone

namespace BootstrapBlazor.Server.Components.Samples.Sockets;

/// <summary>
/// 数据转实体类示例
/// </summary>
public partial class DataEntities
{

}
Original file line number Diff line number Diff line change
Expand Up @@ -209,27 +209,29 @@ void AddSocket(DemoMenuItem item)
{
new()
{
IsNew = true,
Text = Localizer["SocketManualReceive"],
Url = "socket/manual-receive"
},
new()
{
IsNew = true,
Text = Localizer["SocketAutoReceive"],
Url = "socket/auto-receive"
},
new()
{
IsNew = true,
Text = Localizer["DataPackageAdapter"],
Url = "socket/adapter"
},
new()
{
IsNew = true,
Text = Localizer["SocketAutoConnect"],
Url = "socket/auto-connect"
},
new()
{
IsNew = true,
Text = Localizer["SocketDataEntity"],
Url = "socket/data-entity"
}
};
AddBadge(item, count: 1);
Expand Down
7 changes: 7 additions & 0 deletions src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -4841,6 +4841,7 @@
"SocketManualReceive": "Manual Receive",
"DataPackageAdapter": "DataPackageAdapter",
"SocketAutoConnect": "Reconnect",
"SocketDataEntity": "DataEntity",
"NetworkMonitor": "Network Monitor"
},
"BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": {
Expand Down Expand Up @@ -7121,6 +7122,12 @@
"NormalTitle": "Basic usage",
"NormalIntro": "Enable automatic reconnection by setting <code>IsAutoReconnect</code>"
},
"BootstrapBlazor.Server.Components.Samples.Sockets.DataEntities": {
"DataEntityTitle": "Socket Auto Entity",
"DataEntityDescription": "After receiving the communication data, it is automatically converted into the entity class required by the business",
"NormalTitle": "Basic usage",
"NormalIntro": "Enable automatic data conversion through the <code>SocketDataConverterAttribute</code> tag"
},
"BootstrapBlazor.Server.Components.Samples.NetworkMonitors": {
"NetworkMonitorTitle": "NetworkMonitor",
"NetworkMonitorDescription": "Use the browser native API <code>navigator.connection</code> to display the current network status in real time",
Expand Down
7 changes: 7 additions & 0 deletions src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -4841,6 +4841,7 @@
"SocketManualReceive": "手动接收数据",
"DataPackageAdapter": "数据处理器",
"SocketAutoConnect": "自动重连",
"SocketDataEntity": "通讯数据转实体类",
"NetworkMonitor": "网络状态 NetworkMonitor"
},
"BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": {
Expand Down Expand Up @@ -7121,6 +7122,12 @@
"NormalTitle": "基本用法",
"NormalIntro": "通过设置 <code>IsAutoReconnect</code> 开启自动重连机制"
},
"BootstrapBlazor.Server.Components.Samples.Sockets.DataEntities": {
"DataEntityTitle": "Socket 数据转化实体类",
"DataEntityDescription": "接收到通讯数据后自动转成业务需要的实体类",
"NormalTitle": "基本用法",
"NormalIntro": "通过 <code>SocketDataConverterAttribute</code> 标签开启数据自动转换功能"
},
"BootstrapBlazor.Server.Components.Samples.NetworkMonitors": {
"NetworkMonitorTitle": "NetworkMonitor 网络状态",
"NetworkMonitorDescription": "使用浏览器原生 api <code>navigator.connection</code> 实时显示当前网络状态",
Expand Down
1 change: 1 addition & 0 deletions src/BootstrapBlazor.Server/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@
"socket/auto-receive": "Sockets\\AutoReceives",
"socket/adapter": "Sockets\\Adapters",
"socket/auto-connect": "Sockets\\AutoReconnects",
"socket/data-entity": "Sockets\\DataEntities",
"network-monitor": "NetworkMonitors"
},
"video": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public abstract class SocketDataConverterBase<TEntity> : ISocketDataConverter<TE
/// <param name="entity"></param>
protected virtual bool Parse(ReadOnlyMemory<byte> data, TEntity entity)
{
// 使用 SocketDataFieldAttribute 特性获取数据转换规则
// 使用 SocketDataPropertyAttribute 特性获取数据转换规则
var ret = false;
if (entity != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ public class SocketDataDoubleBigEndianConverter : ISocketDataPropertyConverter
public object? Convert(ReadOnlyMemory<byte> data)
{
double ret = 0;
Span<byte> paddedSpan = stackalloc byte[8];
data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
if (BinaryPrimitives.TryReadDoubleBigEndian(paddedSpan, out var v))
if (data.Length <= 8)
{
ret = v;
Span<byte> paddedSpan = stackalloc byte[8];
data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
if (BinaryPrimitives.TryReadDoubleBigEndian(paddedSpan, out var v))
{
ret = v;
}
}
return ret;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ public class SocketDataDoubleLittleEndianConverter : ISocketDataPropertyConverte
public object? Convert(ReadOnlyMemory<byte> data)
{
double ret = 0;
Span<byte> paddedSpan = stackalloc byte[8];
data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
if (BinaryPrimitives.TryReadDoubleLittleEndian(paddedSpan, out var v))
if (data.Length <= 8)
{
ret = v;
Span<byte> paddedSpan = stackalloc byte[8];
data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
if (BinaryPrimitives.TryReadDoubleLittleEndian(paddedSpan, out var v))
{
ret = v;
}
}
return ret;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ public class SocketDataInt16BigEndianConverter : ISocketDataPropertyConverter
public object? Convert(ReadOnlyMemory<byte> data)
{
short ret = 0;
Span<byte> paddedSpan = stackalloc byte[2];
data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
if (BinaryPrimitives.TryReadInt16BigEndian(paddedSpan, out var v))
if (data.Length <= 2)
{
ret = v;
Span<byte> paddedSpan = stackalloc byte[2];
data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
if (BinaryPrimitives.TryReadInt16BigEndian(paddedSpan, out var v))
{
ret = v;
}
}
return ret;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ public class SocketDataInt16LittleEndianConverter : ISocketDataPropertyConverter
public object? Convert(ReadOnlyMemory<byte> data)
{
short ret = 0;
Span<byte> paddedSpan = stackalloc byte[2];
data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
if (BinaryPrimitives.TryReadInt16LittleEndian(paddedSpan, out var v))
if (data.Length <= 2)
{
ret = v;
Span<byte> paddedSpan = stackalloc byte[2];
data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
if (BinaryPrimitives.TryReadInt16LittleEndian(paddedSpan, out var v))
{
ret = v;
}
}
return ret;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ public class SocketDataInt32BigEndianConverter : ISocketDataPropertyConverter
public object? Convert(ReadOnlyMemory<byte> data)
{
var ret = 0;
Span<byte> paddedSpan = stackalloc byte[4];
data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);

if (BinaryPrimitives.TryReadInt32BigEndian(paddedSpan, out var v))
if (data.Length <= 4)
{
ret = v;
Span<byte> paddedSpan = stackalloc byte[4];
data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);

if (BinaryPrimitives.TryReadInt32BigEndian(paddedSpan, out var v))
{
ret = v;
}
}
return ret;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ public class SocketDataInt32LittleEndianConverter : ISocketDataPropertyConverter
public object? Convert(ReadOnlyMemory<byte> data)
{
var ret = 0;
Span<byte> paddedSpan = stackalloc byte[4];
data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);

if (BinaryPrimitives.TryReadInt32LittleEndian(paddedSpan, out var v))
if (data.Length <= 4)
{
ret = v;
Span<byte> paddedSpan = stackalloc byte[4];
data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);

if (BinaryPrimitives.TryReadInt32LittleEndian(paddedSpan, out var v))
{
ret = v;
}
}
return ret;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ public class SocketDataInt64BigEndianConverter : ISocketDataPropertyConverter
public object? Convert(ReadOnlyMemory<byte> data)
{
long ret = 0;
Span<byte> paddedSpan = stackalloc byte[8];
data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);

if (BinaryPrimitives.TryReadInt64BigEndian(paddedSpan, out var v))
if (data.Length <= 8)
{
ret = v;
Span<byte> paddedSpan = stackalloc byte[8];
data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);

if (BinaryPrimitives.TryReadInt64BigEndian(paddedSpan, out var v))
{
ret = v;
}
}
return ret;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ public class SocketDataInt64LittleEndianConverter : ISocketDataPropertyConverter
public object? Convert(ReadOnlyMemory<byte> data)
{
long ret = 0;
Span<byte> paddedSpan = stackalloc byte[8];
data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);

if (BinaryPrimitives.TryReadInt64LittleEndian(paddedSpan, out var v))
if (data.Length <= 8)
{
ret = v;
Span<byte> paddedSpan = stackalloc byte[8];
data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);

if (BinaryPrimitives.TryReadInt64LittleEndian(paddedSpan, out var v))
{
ret = v;
}
}
return ret;
}
Expand Down
Loading
Loading