first commit

This commit is contained in:
2026-09-02 16:31:50 +08:00
commit a461727193
911 changed files with 692450 additions and 0 deletions
@@ -0,0 +1,33 @@
using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Transport.Channels;
using JSMachine.WMS.Infrastructure.Helper;
using System;
using System.Collections.Generic;
namespace JSMachine.WMS.Netty.NettyAsServer.Codec
{
public class Decoder : ByteToMessageDecoder
{
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
{
if (input != null)
{
int length = input.ReadableBytes;
byte[] bytes = new byte[length];
input.ReadBytes(bytes);
try
{
//IMessagePackage message = MessagePackageMange.Instance.BinaryDeserializeToObject(bytes);
output.Add(null);
}
catch (Exception ex)
{
LogHelper.Error($"解码远程地址:{context.Channel.RemoteAddress}----发送的数据异常");
}
input.Clear();
}
}
}
}
@@ -0,0 +1,28 @@
using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Transport.Channels;
using JSMachine.WMS.Infrastructure.Helper;
using System;
using System.Text;
namespace JSMachine.WMS.Netty.NettyAsServer.Codec
{
public class Encoder : MessageToByteEncoder<string>
{
protected override void Encode(IChannelHandlerContext context, string message, IByteBuffer output)
{
try
{
byte[] body = Encoding.UTF8.GetBytes(message);
byte[] tail = Encoding.UTF8.GetBytes("\r");
output.WriteBytes(body);
output.WriteBytes(tail);
}
catch (Exception ex)
{
LogHelper.Error($"TCP编码异常,异常信息 {ex.Message}");
}
}
}
}
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Netty.NettyAsServer.Command
{
/// <summary>
/// 扫码命令
/// </summary>
public class BarCodeReadCmd : BaseCmd
{
public override string Msg { get; set; } = "LON\r\n";
}
}
@@ -0,0 +1,10 @@
namespace JSMachine.WMS.Netty.NettyAsServer.Command
{
/// <summary>
/// 条码上传命令
/// </summary>
public class BarCodeUploadCmd : BaseCmd
{
}
}
@@ -0,0 +1,14 @@
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Netty.NettyAsServer.Command
{
public class BaseCmd
{
public virtual string Msg { get; set; }
}
}
@@ -0,0 +1,52 @@
using JSMachine.WMS.Common;
using JSMachine.WMS.Common.Cache.Model;
using JSMachine.WMS.Common.Cache;
using JSMachine.WMS.Common.Dto.Http.In;
using JSMachine.WMS.Netty.NettyAsServer.Command;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
namespace JSMachine.WMS.Netty.NettyAsServer.CommandHandler
{
public class BarCodeCmdHandlerL : BaseCmdhandler
{
public async override Task<bool> Handle(BaseCmd msg)
{
return await Task.Run(() =>
{
if (msg is BarCodeUploadCmd barCodeUploadCmd)
{
GlobalMemoryCache.AddCach<string, BarCodeInfo>(
RemoteIPInfo.IP,
new BarCodeInfo
{
AddTime = DateTime.Now,
BarCode = barCodeUploadCmd.Msg,
IP = RemoteIPInfo.IP
},
new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(2),
SlidingExpiration = TimeSpan.FromSeconds(1)
});
//bool? exist = Global.AppSettings.PaperFeedConfig.CodeScannerConfig?.Any(p => p.IP == RemoteIPInfo.IP);
//IEventAggregator.GetEvent<UploadBarCodeEvent>().Publish(new BarCodeUploadParam
//{
// BarCode = barCodeUploadCmd.Msg,
// BusinessType = exist != null && exist.Value ? "0" : "1"
//});
return true;
}
return false;
});
}
}
}
@@ -0,0 +1,21 @@
using DotNetty.Transport.Channels;
using JSMachine.WMS.Netty.Common.Util;
using JSMachine.WMS.Netty.NettyAsServer.Command;
using Prism.Events;
using Prism.Ioc;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
namespace JSMachine.WMS.Netty.NettyAsServer.CommandHandler
{
[InheritedExport("NettyAsServer")]
public abstract class BaseCmdhandler
{
protected static IEventAggregator IEventAggregator=ContainerLocator.Container.Resolve<IEventAggregator>();
public IChannelHandlerContext Channel { get; set; }
public IPInfo RemoteIPInfo => Channel.ToIPInfo(AddressOnwer.Remote);
public abstract Task<bool> Handle(BaseCmd msg);
}
}
@@ -0,0 +1,81 @@
using DotNetty.Buffers;
using DotNetty.Handlers.Timeout;
using DotNetty.Transport.Channels;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.Netty.Common.Util;
using JSMachine.WMS.Netty.NettyAsServer.Transport;
using System;
namespace JSMachine.WMS.Netty.NettyAsServer
{
public class EchoServerChannelHandler : ChannelHandlerAdapter
{
private NettyServerTransport NettyTransport;
public override void ChannelActive(IChannelHandlerContext context)
{
LogHelper.Info($"客户端 {context.ToIPInfo(AddressOnwer.Remote).IP} 已连接");
NettyTransport = new NettyServerTransport(context);
base.ChannelActive(context);
}
public override void ChannelRead(IChannelHandlerContext context, object message)
{
//LogsTool.Default.Info("收到服务端TCP信息");
if(message is IByteBuffer byteBuffer)
NettyTransport.ChannelReadHandle(byteBuffer);
}
public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();
public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
{
//LogsTool.Default.Error("Exception: " + exception);
TransportManager.RemoveInActiveChannel(context);
context.CloseAsync();
}
public override void HandlerAdded(IChannelHandlerContext context)
{
base.HandlerAdded(context);
}
public override void HandlerRemoved(IChannelHandlerContext context)
{
LogHelper.Warn($"客户端 {context.ToIPInfo(AddressOnwer.Remote).IP} 下线.");
TransportManager.RemoveInActiveChannel(context);
base.HandlerRemoved(context);
}
public override void UserEventTriggered(IChannelHandlerContext context, object evt)
{
IdleStateEvent eventState = evt as IdleStateEvent;
if (eventState.State == IdleState.ReaderIdle)
{
//没有任何读取
RemoveInactiveChannel();
}
else if (eventState.State == IdleState.WriterIdle)
{
//没有任何写入
RemoveInactiveChannel();
}
else
if (eventState.State == IdleState.AllIdle)
{
//没有任何交互
RemoveInactiveChannel();
}
base.UserEventTriggered(context, evt);
void RemoveInactiveChannel()
{
context.CloseAsync();
TransportManager.RemoveInActiveChannel(context);
}
}
}
}
@@ -0,0 +1,67 @@
using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Handlers.Timeout;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using JSMachine.WMS.Common;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.Netty.NettyAsServer.Codec;
using System;
using System.Text;
using System.Threading.Tasks;
using Encoder = JSMachine.WMS.Netty.NettyAsServer.Codec.Encoder;
namespace JSMachine.WMS.Netty.NettyAsServer
{
public static class TcpServerEngine
{
private static ServerBootstrap SocketBootstrap = new();
private static IEventLoopGroup BossGroup => new MultithreadEventLoopGroup(2);
private static IEventLoopGroup WorkGroup = new MultithreadEventLoopGroup();
public static async void InitEngine(int port)
{
try
{
LogHelper.Info("正在启动TCP服务端-----------");
SocketBootstrap
.Group(BossGroup, WorkGroup)
.Channel<TcpServerSocketChannel>()
.Option(ChannelOption.TcpNodelay, true)
//.Option(ChannelOption.ConnectTimeout, TimeSpan.FromSeconds(1000))
.Option(ChannelOption.SoKeepalive, true)
//以下两种方式都是设置一次性能接收的数据包大小
//.Option(ChannelOption.SoRcvbuf, 4 * 1024)
.Option(ChannelOption.RcvbufAllocator, new FixedRecvByteBufAllocator(8 * 1024))
.ChildHandler(new ActionChannelInitializer<ISocketChannel>(channel =>
{
IChannelPipeline pipeline = channel.Pipeline;
//心跳超时时间配置
//pipeline.AddLast(new IdleStateHandler(100,100,100));
IByteBuffer delimiter = Unpooled.WrappedBuffer(Encoding.UTF8.GetBytes("[CR]\r\n"));
//简单的协议可以直接使用自带的分隔符解码器
pipeline.AddLast("framing-dec", new DelimiterBasedFrameDecoder(4000, true, delimiter));
//自定义编码器
pipeline.AddLast(new Encoder());
pipeline.AddLast("echo", new EchoServerChannelHandler());
}));
await SocketBootstrap.BindAsync(port);
LogHelper.Info("TCP服务启动成功");
}
catch (Exception ex)
{
LogHelper.Error($"TCP服务启动失败,错误信息 {ex.Message}");
await Task.WhenAll(
BossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)),
WorkGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)));
}
}
}
}
@@ -0,0 +1,69 @@
using DotNetty.Buffers;
using DotNetty.Transport.Channels;
using JSMachine.WMS.Netty.Common.Util;
using JSMachine.WMS.Netty.NettyAsServer.Command;
using JSMachine.WMS.Netty.NettyAsServer.CommandHandler;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Netty.NettyAsServer.Transport
{
public class NettyServerTransport
{
public IChannelHandlerContext ChannelHandlerContext;
public IPInfo RemoteIpInfo;
[ImportMany("NettyAsServer")]
private List<BaseCmdhandler> AllCmds { get; set; }
private Lazy<CompositionContainer> _Container = new(() =>
{
AggregateCatalog catalog = new();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
return new CompositionContainer(catalog);
});
public NettyServerTransport(IChannelHandlerContext channel)
{
_Container.Value.ComposeParts(this);
ChannelHandlerContext = channel;
foreach (BaseCmdhandler cmd in AllCmds)
{
cmd.Channel = channel;
}
RemoteIpInfo = channel.ToIPInfo(AddressOnwer.Remote);
if (TransportManager.DicClientChannels.ContainsKey(RemoteIpInfo.IP))
TransportManager.DicClientChannels.TryRemove(RemoteIpInfo.IP,out IChannelHandlerContext channelHandlerContext);
TransportManager.DicClientChannels.TryAdd(RemoteIpInfo.IP, channel);
}
public Task ChannelReadHandle(IByteBuffer byteBuffer)
{
int length = byteBuffer.ReadableBytes;
byte[] array = new byte[length];
byteBuffer.GetBytes(byteBuffer.ReaderIndex, array);
string msg = Encoding.UTF8.GetString(array);
return Task.Run(async () =>
{
foreach (BaseCmdhandler cmd in AllCmds)
{
bool isMsgHasBeenHandled = await cmd.Handle(new BarCodeUploadCmd { Msg = msg });
if (isMsgHasBeenHandled)
break;
}
});
}
}
}
@@ -0,0 +1,132 @@
using DotNetty.Buffers;
using DotNetty.Transport.Channels;
using JSMachine.WMS.Common.Cache;
using JSMachine.WMS.Common.Cache.Model;
using JSMachine.WMS.Infrastructure.Helper;
using JSMachine.WMS.Netty.NettyAsServer.Command;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace JSMachine.WMS.Netty.NettyAsServer.Transport
{
public static class TransportManager
{
/// <summary>
/// 已连接的客户端
/// key-ip 一个客户端仅允许一个连接
/// </summary>
public static readonly ConcurrentDictionary<string, IChannelHandlerContext> DicClientChannels = new();
/// <summary>
/// 移除有问题通道
/// </summary>
/// <param name="channel"></param>
public static void RemoveInActiveChannel(IChannelHandlerContext channel)
{
try
{
DicClientChannels
.Where(p => p.Value == channel)
.Select(p => p.Key)
.ToList()
?.ForEach(p => DicClientChannels.Remove(p, out IChannelHandlerContext channelHandlerContext));
}
catch (Exception ex)
{
LogHelper.Error($"移除有问题通道出错: {ex.Message} \n {ex.StackTrace}");
}
}
/// <summary>
/// 指定客户端发送消息
/// </summary>
/// <param name="ip"></param>
/// <param name="msg"></param>
/// <returns></returns>
public async static Task<bool> SendMsg(string ip, string msg)
{
var result = false;
try
{
if (!DicClientChannels.ContainsKey(ip))
return result;
IChannelHandlerContext channel = DicClientChannels[ip];
await channel.Channel.WriteAndFlushAsync(msg);
result = true;
}
catch (Exception ex)
{
LogHelper.Error($"指定客户端发送消息出错: {ex.Message} \n {ex.StackTrace}");
}
return result;
}
/// <summary>
/// 发送广播消息
/// </summary>
/// <param name="messagePackage"></param>
public static void BroadcastMsg(IByteBuffer messagePackage)
{
try
{
DicClientChannels.Keys.ToList().ForEach(async key =>
{
await DicClientChannels[key].Channel.WriteAndFlushAsync(messagePackage);
});
}
catch (Exception ex)
{
LogHelper.Error($"发送广播消息出错: {ex.Message} \n {ex.StackTrace}");
}
}
/// <summary>
/// 发送扫码命令并获取返回结果
/// </summary>
/// <param name="ip">具体扫码器的IP</param>
/// <returns></returns>
public async static Task<BarCodeInfo> SendReadBarCodeCmd(string ip)
{
var barCodeInfo = new BarCodeInfo();
try
{
string msg = new BarCodeReadCmd().Msg;
bool bRet = await SendMsg(ip, msg);
if (!bRet)
{
LogHelper.Error($"向 {ip} 发送条码扫描命令失败");
return null;
}
DateTime sendTime = DateTime.Now;
int count = 0;
while (true)
{
count++;
Thread.Sleep(100);
barCodeInfo = GlobalMemoryCache.GetCach<string, BarCodeInfo>(ip);
if (barCodeInfo == null || barCodeInfo.AddTime < sendTime)
{
if (count < 20)
continue;
else
return null;
}
}
}
catch (Exception ex)
{
LogHelper.Error($"向 {ip} 发送条码扫描命令返回结果失败{ex.Message} \n {ex.StackTrace}");
}
return barCodeInfo;
}
}
}