2024-08-21 16:50:14 +08:00

268 lines
9.4 KiB
C#

using Abp.Dependency;
using DotNetty.Codecs.Http;
using DotNetty.Codecs.Http.WebSockets;
using DotNetty.Common;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Groups;
using DotNetty.Transport.Channels.Sockets;
using DotNettyHelper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime;
using System.Runtime.InteropServices;
using ToolLibrary.LogHelper;
using Yunda.ISAS.DataMonitoringServer.WebSocket.Model;
namespace Yunda.ISAS.DataMonitoringServer.WebSocket.DotNetty.Server
{
public class DotNettyWebSocketServer : ISingletonDependency
{
public DotNettyWebSocketServer()
{
ResourceLeakDetector.Level = ResourceLeakDetector.DetectionLevel.Simple;
}
private static volatile Dictionary<GroupTypeEnum, IChannelGroup> _dicChannelGroup = new Dictionary<GroupTypeEnum, IChannelGroup>();
public event ExceptionCaughtDelegate ExceptionCaughtEvent;
public event HandlerDelegate HandlerAddedEvent;
public event HandlerDelegate HandlerRemovedEvent;
public event ReceiveMessageDelegate ReceiveMessageEvent;
public Dictionary<GroupTypeEnum, IChannelGroup> GetDictionaryChannelGroup()
{
return _dicChannelGroup;
}
private readonly object lockObj = new object();
public void AddChannel(GroupTypeEnum groupClass, IChannelHandlerContext Context)
{
lock (lockObj)
{
//if (groupClass == GroupTypeEnum.None) return _dotNettyWebSocketServer;
IChannel channel = null;
foreach (var keyValuePair in _dicChannelGroup)
{
if (keyValuePair.Key == groupClass) continue;
channel = keyValuePair.Value.Where(kv => kv.RemoteAddress == Context.Channel.RemoteAddress).FirstOrDefault();
if (channel != null)
{
keyValuePair.Value.Remove(channel);
break;
}
}
if (!_dicChannelGroup.ContainsKey(groupClass))
{
_dicChannelGroup.Add(groupClass, new DefaultChannelGroup(Context.Executor));
}
}
//if (_dicChannelGroup[groupClass].Where(kv => kv.RemoteAddress == Context.Channel.RemoteAddress).Any())
// return _dotNettyWebSocketServer;
_dicChannelGroup[groupClass].Add(Context.Channel);
//return _dotNettyWebSocketServer;
}
private IChannel bootstrapChannel = null;
private IEventLoopGroup bossGroup = null;
private IEventLoopGroup workGroup = null;
private string _websocketPath = "all";
public async void RunServerAsync(string path, int port)
{
_websocketPath = path;
//在这个模式下,垃圾回收会更频繁地运行,以减小每次回收的停顿时间。这适用于对低延迟要求极高的应用程序。
GCSettings.LatencyMode = GCLatencyMode.LowLatency;
//if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
//{
// //调整垃圾收集器侵入应用程序的时间。
// GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency;
//}
bossGroup = new MultithreadEventLoopGroup();
workGroup = new MultithreadEventLoopGroup();
try
{
var bootstrap = new ServerBootstrap();
bootstrap
.Group(bossGroup, workGroup)
.Channel<TcpServerSocketChannel>()
.Option(ChannelOption.SoBacklog, 500)
.ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
{
IChannelPipeline pipeline = channel.Pipeline;
pipeline.AddLast(new HttpServerCodec());
pipeline.AddLast(new HttpObjectAggregator(65536));
pipeline.AddLast(GetWebSocketServerHandler());
}));
bootstrapChannel = await bootstrap.BindAsync(IPAddress.Any, port);
}
catch (Exception ex)
{
throw ex;
}
}
private WebSocketServerHandler GetWebSocketServerHandler()
{
WebSocketServerHandler _wsHandler = new WebSocketServerHandler(_websocketPath);
_wsHandler.ExceptionCaughtEvent += ExceptionCaught;
_wsHandler.HandlerAddedEvent += HandlerAdded;
_wsHandler.HandlerRemovedEvent += HandlerRemoved;
_wsHandler.ReceiveMessageEvent += ReceiveMessage;
return _wsHandler;
}
private void HandlerRemoved(HandlerEventArgs hanlerEventArgs)
{
try
{
HandlerRemovedEvent?.Invoke(hanlerEventArgs);
}
catch (Exception ex)
{
Log4Helper.Error(this.GetType(), "客户端下线",ex);
}
}
/// <summary>
/// 处理客户端报错
/// </summary>
/// <param name="exceptionCaughtEventArgs"></param>
private void ExceptionCaught(ExceptionCaughtEventArgs exceptionCaughtEventArgs)
{
try
{
ExceptionCaughtEvent?.Invoke(exceptionCaughtEventArgs);
}
catch (Exception ex)
{
Log4Helper.Error(this.GetType(), "客户端报错", ex);
}
}
/// <summary>
/// 处理客户端接入
/// </summary>
/// <param name="hanlerEventArgs"></param>
private void HandlerAdded(HandlerEventArgs hanlerEventArgs)
{
try
{
AddChannel(GroupTypeEnum.None, hanlerEventArgs.Context);
HandlerAddedEvent?.Invoke(hanlerEventArgs);
}
catch (Exception ex)
{
Log4Helper.Error(this.GetType(), "处理客户端接入", ex);
}
}
public async void CloseServerAsync()
{
try
{
if (bootstrapChannel != null)
await bootstrapChannel.CloseAsync();
}
catch (Exception ex)
{
Log4Helper.Error(this.GetType(), "关闭websocket", ex);
}
finally
{
if (workGroup != null)
{
await workGroup.ShutdownGracefullyAsync();
}
if (bossGroup != null)
{
await bossGroup.ShutdownGracefullyAsync();
}
}
}
/// <summary>
/// 群发消息
/// </summary>
/// <param name="msg"></param>
public async void Send(DataMonitorMessageModel msg)
{
if (msg.GroupType == GroupTypeEnum.None || !_dicChannelGroup.ContainsKey(msg.GroupType))
return;
WebSocketFrame frame = new TextWebSocketFrame(msg.ToJson());
//if (msg.GroupType == GroupTypeEnum.None)
//{
// foreach (IChannelGroup group in _dicChannelGroup.Values)
// {
// if (!group.Any()) continue;
// group.WriteAndFlushAsync(frame);
// }
// return;
//}
try
{
if (_dicChannelGroup.ContainsKey(GroupTypeEnum.All))
foreach (IChannelGroup group in _dicChannelGroup.Values)
{
if (!group.Any()) continue;
await group.WriteAndFlushAsync(frame.Copy());
}
//_dicChannelGroup[GroupTypeEnum.All].WriteAndFlushAsync(frame.Copy());
if (_dicChannelGroup.ContainsKey(msg.GroupType))
await _dicChannelGroup[msg.GroupType].WriteAndFlushAsync(frame.Copy());
//_dicChannelGroup[GroupTypeEnum.All].WriteAndFlushAsync(frame);
}
catch (Exception ex)
{
Log4Helper.Error(this.GetType(), "websocket群发消息错误", ex);
}
}
/// <summary>
/// 给指定客户端发送消息
/// </summary>
/// <param name="context"></param>
/// <param name="msg"></param>
public async void SendAsync(IChannelHandlerContext context, DataMonitorMessageModel msg)
{
try
{
WebSocketFrame frame = new TextWebSocketFrame(msg.ToJson());
await context.WriteAndFlushAsync(frame);
}
catch (Exception ex)
{
Log4Helper.Error(this.GetType(), "websocket给指定客户端发送消息错误", ex);
}
}
private void ReceiveMessage(ReceiveMessageEventArgs<DataMonitorMessageModel> recMsgEventArgs)
{
try
{
if (recMsgEventArgs.Message == null) return;
AddChannel(recMsgEventArgs.Message.GroupType, recMsgEventArgs.Context);
ReceiveMessageEvent?.Invoke(recMsgEventArgs);
}
catch (Exception ex)
{
Log4Helper.Error(this.GetType(), "websocket收取数据错误", ex);
}
}
}
}