C# DotNetty (1) EchoServer

1.安装核心库
工具-NuGet包管理器-管理解决方案的NuGet程序包
在这里插入图片描述
2.搜索DotNetty,下载以下包:
在这里插入图片描述
3.DotNetty使用

GitHub上有很多例子,先贴地址:
https://github.com/Azure/DotNetty/tree/dev/examples
将整个项目下载下来之后即可查看案例

关于DotNetty相关的介绍:
.NET Core微服务之路:利用DotNetty实现一个简单的通信过程

资料不是很多,但是大部分可以参考java 的Netty,名字可能大小写有点出入,基本上都能对应上的

以上述链接的echo应答式为例修改:

服务端

using DotNetty.Codecs;
using DotNetty.Handlers.Logging;
using DotNetty.Handlers.Tls;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using DotNetty.Transport.Libuv;
using DotNetty.Buffers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using DotNetty.Handlers.Timeout;

namespace LocateAnalyzer.DotNetty
{
    class EchoServer
    {
        public static Dictionary<String, IChannel> Channels = new Dictionary<String, IChannel>();
        public static Dictionary<String, IChannel> ServerChannels = new Dictionary<String, IChannel>();
        private const int Read_TimeOut = 10;
        private const int Write_TimeOut = 30;
        private const int TimeOut = 10;
        public static async Task RunServerAsync(Boolean UseLibuv, Boolean IsSsl, int Port)
        {
            IEventLoopGroup bossGroup;
            IEventLoopGroup workerGroup;

            if (UseLibuv)
            {
                var dispatcher = new DispatcherEventLoopGroup();
                bossGroup = dispatcher;
                workerGroup = new WorkerEventLoopGroup(dispatcher);
            }
            else
            {
                bossGroup = new MultithreadEventLoopGroup(1);
                workerGroup = new MultithreadEventLoopGroup();
            }

            X509Certificate2 tlsCertificate = null;
            if (IsSsl)
            {
                tlsCertificate = new X509Certificate2(Path.Combine(AppContext.BaseDirectory, "dotnetty.com.pfx"), "password");
            }
            try
            {
                var bootstrap = new ServerBootstrap();
                bootstrap.Group(bossGroup, workerGroup);

                if (UseLibuv)
                {
                    bootstrap.Channel<TcpServerChannel>();
                }
                else
                {
                    bootstrap.Channel<TcpServerSocketChannel>();
                }

                bootstrap
                    .Option(ChannelOption.SoBacklog, 100)
                    .Handler(new LoggingHandler("SRV-LSTN"))
                    .ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
                    {
                        IChannelPipeline pipeline = channel.Pipeline;
                        if (tlsCertificate != null)
                        {
                            pipeline.AddLast("tls", TlsHandler.Server(tlsCertificate));
                        }
                        pipeline.AddLast(new LoggingHandler("SRV-CONN"));

                        pipeline.AddLast("framing-dec", new LengthFieldBasedFrameDecoder(ByteOrder.LittleEndian, Int32.MaxValue, 4, 4, 0, 0, true));
                        //添加变长协议的Decoder
                        //参数说明:
                        /**
                         * 1.ByteOrder 字节顺序 大端序(低位在后) 或 小端序(低位在前)
                         * 2.maxFrameLength 包体最大长度
                         * 3.lengthFieldOffset 长度字段偏移量 即长度字段的第一个字节前面还有多少个字节
                         * 4.lengthFieldLength 长度字段占字节数
                         * 5.lengthAdjustment 长度补偿 The compensation value to add to the value of the length field.
                         * 6.initialBytesToStrip 解码时跳过的字节数 the number of first bytes to strip out from the decoded frame.
                         * 7.failFast 为true时解码失败抛出异常
                         */
                        pipeline.AddLast("echo", new EchoServerHandler());//自定义业务处理器
                        pipeline.AddLast("framing-enc", new EchoServerEncoder());//自定义编码器
                        //闲置状态处理器及心跳检测
                        pipeline.AddLast("framing-IdleState", new IdleStateHandler(Read_TimeOut, Write_TimeOut, TimeOut));
                        pipeline.AddLast("framing-HeartBeating", new EchoHeartBeating());
                    }));

                var chan = await bootstrap.BindAsync(Port);
                ServerChannels[chan.LocalAddress.ToString()] = chan;
                Console.ReadLine();
                Console.WriteLine("Server Stop");
                await chan.CloseAsync();
            }
            finally
            {
                await Task.WhenAll(
                    bossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)),
                    workerGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)));
            }
        }
        #region Send To Client
        public static void Write(String Address, Object[] msg)
        {
            List<byte> bytes = new List<byte>();
            foreach (var m in msg)
                bytes.AddRange(Util.SocketUtil.StructToBytes(m));
            Channels[Address].WriteAndFlushAsync(bytes.ToArray());
        }
        public static async Task WriteAsync(String Address, Object[] msg)
        {
            List<byte> bytes = new List<byte>();
            foreach (var m in msg)
                bytes.AddRange(Util.SocketUtil.StructToBytes(m));
            await Channels[Address].WriteAndFlushAsync(bytes.ToArray());
        }
        #endregion
        #region Send To AllClient
        public static void WriteAll(Object[] msg)
        {
            List<byte> bytes = new List<byte>();
            foreach (var m in msg)
                bytes.AddRange(Util.SocketUtil.StructToBytes(m));
            foreach (var chan in Channels.Values)
                chan.WriteAndFlushAsync(bytes.ToArray());
        }
        public static async Task WriteAllAsync(Object[] msg)
        {
            List<byte> bytes = new List<byte>();
            foreach (var m in msg)
                bytes.AddRange(Util.SocketUtil.StructToBytes(m));
            foreach (var chan in Channels.Values)
                await chan.WriteAndFlushAsync(bytes.ToArray());
        }
        #endregion
    }
}

注:Util.SocketUtil.StructToBytes为自定义的序列化方法
可参考之前的文章或自定义

EchoServerEncoder:

using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Transport.Channels;

namespace LocateAnalyzer.DotNetty
{
    internal class EchoServerEncoder : MessageToByteEncoder<byte[]>
    {
        protected override void Encode(IChannelHandlerContext context, byte[] message, IByteBuffer output)
        {
            output.WriteBytes(message);
        }
    }
}

EchoHeartBeating:
心跳检测,这个等价于java中的io.netty.channel.ChannelInboundHandlerAdapter

using DotNetty.Transport.Channels;
using DotNetty.Handlers.Timeout;
using System;

namespace LocateAnalyzer.DotNetty
{
    internal class EchoHeartBeating : SimpleChannelInboundHandler<object>
    {
        public override void UserEventTriggered(IChannelHandlerContext context, object evt)
        {
            if (evt.GetType() == typeof(IdleStateEvent))
            {
                var idleEvent = evt as IdleStateEvent;
                if (idleEvent.State == IdleState.ReaderIdle)
                {
                    Console.WriteLine("ReaderIdle:" + DateTime.Now.ToLongTimeString());
                    //context.Channel.CloseAsync();
                    base.UserEventTriggered(context, evt);
                }
                else if (idleEvent.State == IdleState.WriterIdle)
                {
                    Console.WriteLine("WriterIdle:" + DateTime.Now.ToLongTimeString());
                    //context.Channel.CloseAsync();
                    base.UserEventTriggered(context, evt);
                }
                else
                {
                    Console.WriteLine("AllIdle:" + DateTime.Now.ToLongTimeString());
                    context.Channel.CloseAsync();
                    //base.UserEventTriggered(context, evt);
                }
            }
        }

        protected override void ChannelRead0(IChannelHandlerContext ctx, object msg)
        {
            Console.WriteLine("ChannelRead0:" + DateTime.Now.ToLongTimeString());
            throw new NotImplementedException();
        }
    }
}

EchoServerHandler:

using DotNetty.Buffers;
using DotNetty.Transport.Channels;
using System;
using System.Threading.Tasks;

namespace LocateAnalyzer.DotNetty
{
    internal class EchoServerHandler : ChannelHandlerAdapter
    {
        public override void ChannelRegistered(IChannelHandlerContext context)
        {
            Console.WriteLine(context.Channel.RemoteAddress.ToString() + " Client Channel Registered");
            EchoServer.Channels[context.Channel.RemoteAddress.ToString()] = context.Channel;
            base.ChannelRegistered(context);
        }
        public override void ChannelUnregistered(IChannelHandlerContext context)
        {
            Console.WriteLine(context.Channel.RemoteAddress.ToString() + " Client Channel UnRegistered");
            EchoServer.Channels.Remove(context.Channel.RemoteAddress.ToString());
            base.ChannelUnregistered(context);
        }
        public override void ChannelRead(IChannelHandlerContext context, object message)
        {
            IByteBuffer buffer = message as IByteBuffer;
            if (buffer != null)
            {
                int length = buffer.ReadableBytes;
                Console.WriteLine("Recv from " + context.Channel.RemoteAddress.ToString() + "length:" + length);
                var readBuf = buffer.ReadBytes(length);

                for (int i = 0; i < length; i++)
                {
                    Console.Write("0x" + readBuf.ReadByte().ToString("X") + " ");
                }
                Console.WriteLine();
                buffer.DiscardReadBytes();

                CommandPack.CMDLib.Header hdr = new CommandPack.CMDLib.Header()
                {
                    type = 666,
                    length = 8
                };
                //EchoServer.Write(context.Channel.RemoteAddress.ToString(), new object[] { hdr, hdr });
                Console.WriteLine("sendTime:" + DateTime.Now.ToLongTimeString());
                EchoServer.WriteAll(new object[] { hdr, hdr });

            }
        }

        public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();

        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
        {
            Console.WriteLine("Exception: " + exception);
            context.CloseAsync();
        }
    }
}

结果:
在这里插入图片描述在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值