添加库 最少以下两个

DotNetty.Common
DotNetty.Handlers
using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Handlers.Timeout;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using System.Text;

namespace CSharpAllDemo.Tcp
{

    public class DotnettyManager
    {
        // 私有静态实例
        private static readonly DotnettyManager _instance = new DotnettyManager();
        // 公共静态方法,返回实例
        public static DotnettyManager Instance => _instance;

        private Dictionary<string, IChannelHandlerContext> mContextDictory = new();

        private DotnettyManager() { }
        public async Task InitAsync(int port)
        {

            // 主工作线程组,设置为1个线程
            var bossGroup = new MultithreadEventLoopGroup(1);
            // 工作线程组,默认为内核数*2的线程数
            var workerGroup = new MultithreadEventLoopGroup();//声明一个服务端Bootstrap,每个Netty服务端程序,都由ServerBootstrap控制,
                                                              //通过链式的方式组装需要的参数
            var bootstrap = new ServerBootstrap();
            bootstrap
            .Group(bossGroup, workerGroup) // 设置主和工作线程组
            .Channel<TcpServerSocketChannel>() // 设置通道模式为TcpSocket
            .Option(ChannelOption.SoBacklog, 100) // 设置网络IO参数等,这里可以设置很多参数,当然你对网络调优和参数设置非常了解的话,你可以设置,或者就用默认参数吧
            .Option(ChannelOption.SoKeepalive, true)//保持连接
                                                    //.Option(ChannelOption.RcvbufAllocator, new FixedRecvByteBufAllocator(4000))
           .ChildHandler(new ActionChannelInitializer<ISocketChannel>(channel =>
           {
               //工作线程连接器 是设置了一个管道,服务端主线程所有接收到的信息都会通过这个管道一层层往下传输
               //同时所有出栈的消息 也要这个管道的所有处理器进行一步步处理
               IChannelPipeline pipeline = channel.Pipeline;
               pipeline.AddLast(new CommonServerDecoder());
               pipeline.AddLast(new CommonServerEncoder());
               pipeline.AddLast(new IdleStateHandler(5, 0, 0));
               //业务handler ,这里是实际处理业务的Handler
               pipeline.AddLast(new TcpServerHandler());
           }));

            // bootstrap绑定到指定端口的行为 就是服务端启动服务,同样的Serverbootstrap可以bind到多个端口
            IChannel boundChannel = await bootstrap.BindAsync(port);
        }
        public void Send(string deviceId, byte[] message)
        {
            mContextDictory[deviceId]?.WriteAndFlushAsync(message);
        }

    }
    class TcpServerHandler : ChannelHandlerAdapter
    {
        public override void ChannelRead(IChannelHandlerContext context, object message)
        {
            if (message is byte[] o)
            {
                string srt = Encoding.UTF8.GetString(o);
                context.WriteAndFlushAsync(srt);
                //System.Diagnostics.Debug.WriteLine($"byte方式,从服务端接收:{Encoding.UTF8.GetString(o)}");

            }

        }
        public override void UserEventTriggered(IChannelHandlerContext context, object evt)
        {
            if (evt is IdleStateEvent)
            {
                IdleStateEvent e = evt as IdleStateEvent;
                if (e.State == IdleState.AllIdle)
                {
                    //读或者写
                    System.Diagnostics.Debug.WriteLine("==AllIdle==");
                }
                else if (e.State == IdleState.ReaderIdle)
                {
                    //读
                    System.Diagnostics.Debug.WriteLine("==ReaderIdle==");

                }
                else if (e.State == IdleState.WriterIdle)
                {
                    System.Diagnostics.Debug.WriteLine("==WriterIdle==");
                }
            }

        }
    }


    class CommonServerDecoder : ByteToMessageDecoder
    {
        protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
        {
            byte[] array = new byte[input.ReadableBytes];
            input.GetBytes(input.ReaderIndex, array, 0, input.ReadableBytes);
            input.Clear();
            output.Add(array);
        }
    }

    class CommonServerEncoder : MessageToByteEncoder<string>
    {
        protected override void Encode(IChannelHandlerContext context, string message, IByteBuffer output)
        {
            byte[] messageBytes = Encoding.UTF8.GetBytes(message);
            IByteBuffer initialMessage = Unpooled.Buffer(messageBytes.Length);
            initialMessage.WriteBytes(messageBytes);
            output.WriteBytes(initialMessage);
        }
    }



}