1. 程式人生 > >DotNetty關鍵概念及簡單示例(基於NET5)

DotNetty關鍵概念及簡單示例(基於NET5)

# DotNetty關鍵概念及簡單示例(基於NET5) [toc] ## 1.DotNetty 設計的關鍵 非同步和事件驅動是Netty設計的關鍵。 ### 1.1 核心元件 #### 1.1.1 Channel Channel:一個連線就是一個Channel。 Channel是Socket的封裝,提供繫結,讀,寫等操作,降低了直接使用Socket的複雜性。Channel是Socket的抽象,可以被註冊到一個EventLoop上,EventLoop相當於Selector,每一個EventLoop又有自己的處理執行緒。 ![](https://img2020.cnblogs.com/blog/1606616/202012/1606616-20201204154133423-357213429.png) ![](https://img2020.cnblogs.com/blog/1606616/202012/1606616-20201204154144256-1153524790.png) #### 1.1.2 回撥 回撥:通知的基礎。 #### 1.1.3 EventLoop EventLoop 我們之前就講過EventLoop這裡回顧一下: 一個 EventLoopGroup 包含一個或者多個 EventLoop; 一個 EventLoop 在它的生命週期內只和一個 Thread 繫結; 所有由 EventLoop 處理的 I/O 事件都將在它專有的 Thread 上被處理; 一個 Channel 在它的生命週期內只註冊於一個 EventLoop; 一個 EventLoop 可能會被分配給一個或多個 Channel。 ![](https://img2020.cnblogs.com/blog/1606616/202012/1606616-20201204154154619-291275268.png) #### 1.1.4 ChannelHandler ChannelHandler是處理資料的邏輯容器 ChannelInboundHandler是接收並處理入站事件的邏輯容器,可以處理入站資料以及給客戶端以回覆。 #### 1.1.5 ChannelPipeline ChannelPipeline是將ChannelHandler穿成一串的的容器。 ![](https://img2020.cnblogs.com/blog/1606616/202012/1606616-20201204154124922-857924895.jpg) #### 1.1.6 編碼器和解碼器 編碼器和解碼器都實現了ChannelInboundHandler和 ChannelOutboundHandler介面用於處理入站或出站資料。 #### 1.1.7 Bootstrap引導類 + Bootstrap用於引導客戶端,ServerBootstrap用於引導伺服器 + 客戶端引導類只需要一個EventLoopGroup伺服器引導類需要兩個EventLoopGroup。但是在簡單使用中,也可以公用一個EventLoopGroup。為什麼伺服器需要兩個EventLoopGroup呢?是因為伺服器的第一個EventLoopGroup只有一個EventLoop,只含有一個SeverChannel用於監聽本地埠,一旦連線建立,這個EventLoop就將Channel控制權移交給另一個EventLoopGroup,這個EventLoopGroup分配一個EventLoop給Channel用於管理這個Channel。 #### 1.1.8 AbstractByteBuffer IByteBuffer IByteBufferHolder 位元組級操作,工控協議的話大多都是位元組流,我們以前的方式就是拼,大概就是:對照協議這兩個位元組是什麼,後四個位元組表示什麼意思。現在DotNetty提供了一個ByteBuffer來簡化我們對於位元組流的操作。適用工控協議。json格式的,但是底層還是位元組流。 ## 2 DotNetty Nuget包 DotNetty由九個專案構成,在NuGet中都是單獨的包,可以按需引用,其中比較重要的幾個是以下幾個: + DotNetty.Common 是公共的類庫專案,包裝執行緒池,並行任務和常用幫助類的封裝 + DotNetty.Transport 是DotNetty核心的實現 + DotNetty.Buffers 是對記憶體緩衝區管理的封裝 + DotNetty.Codes 是對編碼器解碼器的封裝,包括一些基礎基類的實現,我們在專案中自定義的協議,都要繼承該專案的特定基類和實現 + DotNetty.Handlers 封裝了常用的管道處理器,比如Tls編解碼,超時機制,心跳檢查,日誌等,如果專案中沒有用到可以不引用,不過一般都會用到 ## 3 一個例子 ### 3.1 服務端程式碼示例 #### 3.1.1 服務端配置 配置成伺服器管道,TcpServerSocketChannel,之所以配置成伺服器管道原因是與客戶端管道不同,伺服器管道多了偵聽服務。將服務端的邏輯處理程式碼Handler以pipeline形式新增到channel中去。 ```CSharp using DotNetty.Transport.Bootstrapping; using DotNetty.Transport.Channels; using DotNetty.Transport.Channels.Sockets; using System; using System.Threading.Tasks; namespace EchoServer { class Program { static async Task RunServerAsync() { IEventLoopGroup eventLoop; eventLoop = new MultithreadEventLoopGroup(); try { // 伺服器載入程式 var bootstrap = new ServerBootstrap(); bootstrap.Group(eventLoop); bootstrap.Channel() // 保持長連線 .ChildOption(ChannelOption.SoKeepalive, true); bootstrap.ChildHandler(new ActionChannelInitializer(channel => { IChannelPipeline pipeline = channel.Pipeline; pipeline.AddLast(new EchoServerHandler()); })); IChannel boundChannel = await bootstrap.BindAsync(3000); Console.ReadLine(); await boundChannel.CloseAsync(); } catch (Exception ex) { Console.WriteLine(ex); } finally { await eventLoop.ShutdownGracefullyAsync(); } } static void Main(string[] args) => RunServerAsync().Wait(); } } ``` #### 3.1.2 服務端處理邏輯程式碼 接收連入服務端程式碼的客戶端訊息,並將此訊息重新返回給客戶端。 ```Csharp using DotNetty.Buffers; using DotNetty.Transport.Channels; using System; using System.Text; namespace EchoServer { /// /// 因為伺服器只需要響應傳入的訊息,所以只需要實現ChannelHandlerAdapter就可以了 ///
public class EchoServerHandler : ChannelHandlerAdapter { /// /// 每個傳入訊息都會呼叫 /// 處理傳入的訊息需要複寫這個方法 /// /// /// public override void ChannelRead(IChannelHandlerContext ctx, object msg) { IByteBuffer message = msg as IByteBuffer; Console.WriteLine("收到資訊:" + message.ToString(Encoding.UTF8)); ctx.WriteAsync(message); } /// /// 批量讀取中的最後一條訊息已經讀取完成 ///
/// public override void ChannelReadComplete(IChannelHandlerContext context) { context.Flush(); } /// /// 發生異常 /// /// /// public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) { Console.WriteLine(exception); context.CloseAsync(); } } } ``` ### 3.2 客戶端程式碼示例 #### 3.2.1 客戶端服務配置 配置需要連線的服務端ip地址及其埠號,並且配置客戶端的處理邏輯程式碼以pipeline形式新增到channel中去。 ```Csharp using DotNetty.Transport.Bootstrapping; using DotNetty.Transport.Channels; using DotNetty.Transport.Channels.Sockets; using System; using System.Net; using System.Threading.Tasks; namespace EchoClient { class Program { static async Task RunClientAsync() { var group = new MultithreadEventLoopGroup(); try { var bootstrap = new Bootstrap(); bootstrap .Group(group) .Channel() .Handler(new ActionChannelInitializer(channel => { IChannelPipeline pipeline = channel.Pipeline; pipeline.AddLast(new EchoClientHandler()); })); IChannel clientChannel = await bootstrap.ConnectAsync(new IPEndPoint(IPAddress.Parse("192.168.1.11"), 3000)); Console.ReadLine(); await clientChannel.CloseAsync(); } catch (Exception ex) { Console.WriteLine(ex); } finally { await group.ShutdownGracefullyAsync(); } } static void Main(string[] args) => RunClientAsync().Wait(); } } ``` #### 3.2.2 客戶端處理邏輯程式碼 客戶端連線成功後,向服務端傳送訊息,接受到服務端訊息,對訊息計數再發還給服務端。 ```Csharp using DotNetty.Buffers; using DotNetty.Transport.Channels; using System; using System.Collections.Generic; using System.Text; namespace EchoClient { public class EchoClientHandler : SimpleChannelInboundHandler { public static int i=0; /// /// Read0是DotNetty特有的對於Read方法的封裝 /// 封裝實現了: /// 1. 返回的message的泛型實現 /// 2. 丟棄非該指定泛型的資訊 ///
/// /// protected override void ChannelRead0(IChannelHandlerContext ctx, IByteBuffer msg) { if (msg != null) { i++; Console.WriteLine($"Receive From Server {i}:" + msg.ToString(Encoding.UTF8)); } ctx.WriteAsync(Unpooled.CopiedBuffer(msg)); } public override void ChannelReadComplete(IChannelHandlerContext context) { context.Flush(); } public override void ChannelActive(IChannelHandlerContext context) { Console.WriteLine($"傳送客戶端訊息"); context.WriteAndFlushAsync(Unpooled.CopiedBuffer(Encoding.UTF8.GetBytes($"客戶端訊息!"))); } public override void ExceptionCaught(IChannelHandlerContext context, Exception exception) { Console.WriteLine(exception); context.CloseAsync(); } } } ``` ## 4 最終輸出效果 ![](https://img2020.cnblogs.com/blog/1606616/202012/1606616-20201204154215115-107530385.png) **備註:紅框表示接收到訊息的序號。** ## 5 參考部落格 [DotNetty完全教程](https://blog.csdn.net/nxy_wuhao/category_9469658.html) [.NET Core3.1 Dotnetty實戰](https://www.cnblogs.com/justzhuzhu/tag/dotnetty/) [DotNetty系列](https://blog.csdn.net/qq_34719168/category_8693600.html) [dotNetty modbus系列](https://www.cnblogs.com/victorbu/p/10370890.html) [編解碼框架](https://blog.csdn.net/universaluniversal/article/details/81219036)
版權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結和本宣告。 本文連結:https://www.cnblogs.com/JerryMouseLi/p/14086