DotNetty 高性能NIO通讯模型 服务端和客户端案例版

Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的高性能客户端/服务器 通讯框架。

Netty的优势:.

  1. 1. 并发高

  2. 2. 传输快

  3. 3. 封装好

还有一个叫做 内存零拷贝技术。

整体架构大致如下图:

DotNetty 高性能NIO通讯模型 服务端和客户端案例版

从架构图来看,NIO非阻塞模型的连接数要多许多。

DotNetty

然而,我是偏向.Net技术栈的,所以,我采用DotNetty来使用Netty技术实现通讯的高性能,我也抽时间对里面的细节做了研究,但是,针对于内存零拷贝技术(DMA 内存直接访问),Windows平台好像没有相应的接口,所以,也不了了之,有知道的大佬,也可以告诉我,不吝感激。

实际项目

分为服务端和客户端

服务端 Server

先安装Nuget包

Install-Package DotNetty.Codecs  -Version 0.7.2
Install-Package DotNetty.Transport -Version 0.7.2

然后,实现服务端代码,用第三方就是方便,几行代码就搞定了。

NettyServer.cs

     /// <summary>
    /// 服务
    /// </summary>
    public class NettyServer
    {
        public async Task Listen(int port)
        {
            var boos = new MultithreadEventLoopGroup(1);
            var work = new MultithreadEventLoopGroup();

            var bootstrap = new ServerBootstrap();
            bootstrap.Group(boos, work)
                .Channel<TcpServerSocketChannel>()
                .Option(ChannelOption.SoBacklog, 100)
                .ChildHandler(new ActionChannelInitializer<ISocketChannel>(channel => {
                    IChannelPipeline pipeline = channel.Pipeline;

                    pipeline.AddLast(new ServerMessageHandler());
                }));
            IChannel boundChannel = await bootstrap.BindAsync(port);
        }
    }

ServerMessageHandler.cs

实现具体的业务

    public class ServerMessageHandler : ChannelHandlerAdapter
    {
        public override void ChannelRead(IChannelHandlerContext context, object message)
        {
            var buffer = message as IByteBuffer;
            if (buffer != null)
            {
                string receiveData = buffer.ToString(Encoding.UTF8);
                Console.WriteLine("服务端获取到:" + receiveData);
                byte[] messageBytes = Encoding.UTF8.GetBytes(receiveData);
                IByteBuffer byteBuffer = Unpooled.Buffer(256);
                byteBuffer.WriteBytes(messageBytes);
                context.WriteAndFlushAsync(byteBuffer);
            }
        }
        public override void ChannelReadComplete(IChannelHandlerContext context) => context.Flush();

        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
        {
            context.CloseAsync();
        }
    }

测试运行代码

        static void Main(string[] args)
        {
            var server = new NettyServer();
            server.Listen(9999).Wait();
            Console.WriteLine("服务启动");
            Console.ReadLine();
        }

客户端实现 Client

来先安装Nuget包

Install-Package DotNetty.Codecs  -Version 0.7.2
Install-Package DotNetty.Handlers -Version 0.7.2
Install-Package DotNetty.Transport -Version 0.7.2

客户端实现 NettyClinet.cs

    /// <summary>
    /// 客户端
    /// </summary>
    public class NettyClinet    
    {
        public static IChannel Channel { set; get; }
        public static IEventLoopGroup Group { set; get; }
        public static ClientMessageHandler clientMessageHandler = new ClientMessageHandler();
        public async void Connect(string host, int port)
        {
            Group = new MultithreadEventLoopGroup();

            var bootstrap = new Bootstrap();
            bootstrap
                .Group(Group)
                .Channel<TcpSocketChannel>()
                .Option(ChannelOption.TcpNodelay, true)
                .Handler(new ActionChannelInitializer<ISocketChannel>(channel =>
                {
                    IChannelPipeline pipeline = channel.Pipeline;
                    pipeline.AddLast(clientMessageHandler);
                }));
            Channel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse(host), port));
        }
        public async void Close()
        {
            await Channel.CloseAsync();
            await Group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1));
        }
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="message"></param>
        public bool SendMessage(string message)
        {
            if (message == null)
            {
                return false;
            }
            IByteBuffer buffer = Unpooled.Buffer(256);

            byte[] messageBytes = Encoding.UTF8.GetBytes(message);
            buffer.WriteBytes(messageBytes);
            Channel.WriteAndFlushAsync(buffer);

            return true;
        }
    }

ClientMessageHandler.cs

实现客户端读取数据的业务

    public class ClientMessageHandler : ChannelHandlerAdapter
    {
        public override void ChannelRead(IChannelHandlerContext context, object message)
        {
            var byteBuffer = message as IByteBuffer;

            if (byteBuffer != null)
            {
                var data = byteBuffer.ToString(Encoding.UTF8);
                Console.WriteLine("客户端收到:" + data);
            }
        }

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

        public override void ExceptionCaught(IChannelHandlerContext context, Exception exception)
        {
            Console.WriteLine("Exception: " + exception);
            context.CloseAsync();
        }
    }    public class ClientMessageHandler : ChannelHandlerAdapter
    {
        public override void ChannelRead(IChannelHandlerContext context, object message)
        {
            var byteBuffer = message as IByteBuffer;

            if (byteBuffer != null)
            {
                var data = byteBuffer.ToString(Encoding.UTF8);
                Console.WriteLine("客户端收到:" + data);
            }
        }

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

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

客户端测试

        static void Main(string[] args)
        {
            var clinet = new NettyClinet();
            clinet.Connect("127.0.0.1", 9999);
            Console.WriteLine("客户端开始连接!");
            string content = "";
            while (!(content = Console.ReadLine()).Contains("Exit", StringComparison.InvariantCultureIgnoreCase))
            {
                clinet.SendMessage(content);
            }
            Console.ReadLine();
        }

至此,已经把Netty的服务端与客户端写完了。来看下效果

Netty 通讯效果

先启动服务端,然后,再启动客户端

我们看下效果

效果还是很明显的

DotNetty 高性能NIO通讯模型 服务端和客户端案例版

总结

基于Netty高性能的通讯框架搭建好了,也测试了。至此,已经对大多数通讯有了大致的了解,更加深入的知识点,还需要看文档,看源码,以及有针对性的解决了。

代码地址

https://github.com/kesshei/NettyDemo.git

https://gitee.com/kesshei/NettyDemo.git