Nettyの紹介(NetはDotNettyです)



Introduction Netty



(最初にマイクロソフトに大いに不満を言う)私たちはNET開発を行っており、JAVAがNETTY、SPRING、STRUTS、DUBBO、その他の優れたフレームワークを持つことができることを非常にうらやましく思っています。 、嫌いな鉄鋼ではありません。しかし、最近のNet Coreのリリースにより、Microsoftに属する世界のごく一部をゆっくりと引き戻し、停止してチャットしました。

DotNettyは、Azureチーム(ほぼこのような)JAVAのNetty(現在はNettyの一部)をモデルにしており、Githubの現在のStarはhttps://github.com/Azure/DotNettyで1.8K以上です。コードにはドキュメントがなく、コメントが少数あります。長年Nettyを使用していませんが、NETプログラマーは、プラットフォームでNettyのような強力な通信フレームワークを最終的に使用できることに感謝する必要があります。



従来のコミュニケーションの問題:

共通のアプリケーションまたはクラスライブラリを使用して相互に通信します。たとえば、HTTPクライアントライブラリを使用して、Webサーバーから情報を取得したり、Webサービスを介してリモート呼び出しを実行したりすることがよくあります。

ただし、一般的なプロトコルまたはその実装がニーズを十分に満たしていない場合があります。たとえば、一般的なHTTPサーバーを使用して、大きなファイル、電子メール、および財務情報やマルチプレーヤーデータなどのほぼリアルタイムのメッセージを処理することはできません。いくつかの特別なシナリオを処理するには、高度に最適化されたプロトコルが必要です。たとえば、最適化されたAjaxチャットアプリケーション、メディアストリーミング、または大きなファイル転送を実装したい場合があります。また、まったく新しいプロトコルを設計および実装して、ニーズを正確に実装することもできます。



もう1つの避けられない状況は、レガシーシステムとの相互運用性を確保するためにレガシープロプライエタリプロトコルを処理する必要がある場合です。この場合、アプリケーションの安定性とパフォーマンスを犠牲にすることなく、プロトコルを迅速に実装する方法が重要です。

解決する:

Nettyは、非同期イベント駆動型(非同期イベント駆動型)のサーバーおよびクライアントを提供するネットワークアプリケーションフレームワークであり、高性能でスケーラブルなプロトコルを迅速に開発します。

つまり、NettyはNIOクライアントサーバーフレームワークであり、サーバープロトコルやクライアントプロトコルなどのWebアプリケーションをすばやく簡単に開発できます。 Nettyは、TCPやUDPソケットサービスなどのネットワークプログラムの開発を大幅に簡素化します。



「高速でシンプル」とは、アプリケーションにハードメンテナンスと低パフォーマンスの問題があることを意味するものではありません。 Nettyは、FTP、SMTP、HTTP、および多くのバイナリなどの多くのプロトコル実装から多くの経験を引き出す、適切に設計されたフレームワークです。そして、従来のテキストベースのプロトコル。したがって、Nettyは、柔軟性を失うことなく、開発の容易さ、高性能、および安定性を実現する方法を見つけることに成功しました。

一部のユーザーは、他のネットワークフレームワークにも同じ利点があると主張していることに気付いたかもしれないので、Nettyとその違いを尋ねることができます。答えは、Nettyの哲学デザイン哲学です。 Nettyは当初から、最高のユーザーエクスペリエンスAPIと実装設計をユーザーに提供してきました。このガイドを簡単に読んでNettyを使用できるのは、Nettyの哲学的な設計哲学によるものです。

640?wx_fmt = jpeg

(DotNettyのフレームワークと実装は進行中です。よくわかりませんが、DotNetty関連のAPIインターフェイスを学習して使用するには、Nettyの公式ドキュメントを参照できます。)

DotNettyのいくつかの重要なライブラリ(アセンブリ):

DotNetty.Buffers:メモリバッファ管理のカプセル化。

DotNetty.Codecs:コーデックはカプセル化されており、基礎となる基本クラスのいくつかの実装が含まれています。プロジェクトでカスタマイズするプロトコルは、特定の基本クラスとプロジェクトの実装を継承します。

DotNetty.Codecs.Mqtt:MQTT(Message Queue Telemetry Transport)コーデックはカプセル化されており、基盤となる基本クラスのいくつかの実装が含まれています。

DotNetty.Codecs.Protobuf:Protobufコーデックはカプセル化されており、基礎となる基本クラスのいくつかの実装が含まれています。

DotNetty.Codecs.ProtocolBuffers:ProtocolBuffersコーデックはカプセル化されており、基礎となる基本クラスのいくつかの実装が含まれています。

DotNetty.Codecs.Redis:Redisプロトコルコーデックはカプセル化されており、基盤となる基本クラスのいくつかの実装が含まれています。

DotNetty.Common:共通のクラスライブラリプロジェクト、ラッパースレッドプール、並列タスク、および共通のヘルパークラスのラッパー。

DotNetty.Handlers:Tlsコーデック、タイムアウトメカニズム、ハートビートチェック、ログなどの一般的なパイプラインプロセッサをカプセル化します。

DotNetty.Transport:DotNettyコアの実装、ソケットインフラストラクチャ、通信モード:非同期ノンブロッキング。

DotNetty.Transport.Libuv:DotNettyは、Libuv(高性能、イベント駆動型I / Oライブラリ)に基づく独自のコア実装を実装します。

一般的に使用されるライブラリは、コーデック、共通、ハンドラ、バッファ、トランスポートです。現在、AzureチームはNettyに他のAPI(非公開のNetty APIを含む)を実装しています。待って見てみましょう。

ピア間で直接通信する栗

DotNettyのExampleフォルダーには、Discard Service Instance(Discard)、Answer Service Instance(echo)、Telnet Service Instanceなど、多くの公式の例があります。直接のピアツーピア通信を実現するために、Echoのデモを使用しました。 RPC呼び出しも、Echoに基づいて実装され、受信者(サーバー)コードに直接詳細にコメントします。

/ *

* Nettyは、カスタムプロトコルに基づいて独自の通信パッケージを実行する半製品です。

* Nettyは、TCPやUDPソケットサービスなどのネットワークプログラムの開発を大幅に簡素化します。

*「高速でシンプル」とは、アプリケーションのメンテナンスが困難でパフォーマンスが低いという問題があることを意味するものではありません。

* Nettyは、FTP、SMTP、HTTP、多くのバイナリおよび従来のテキストベースのプロトコルなど、多くのプロトコル実装から多くの経験を引き出す、適切に設計されたフレームワークです。

*したがって、Nettyは、柔軟性を損なうことなく、開発の容易さ、高性能、および安定性を実現する方法を見つけることに成功しました。

* /

名前空間Echo.Server

{{

システムを使用する

System.Threading.Tasksを使用する

DotNetty.Codecsを使用する

DotNetty.Handlers.Loggingを使用する

DotNetty.Transport.Bootstrappingを使用する

DotNetty.Transport.Channelsを使用する

DotNetty.Transport.Libuvを使用する

Examples.Commonを使用する

静的クラスプログラム

{{

静的非同期タスクRunServerAsync()

{{

ExampleHelper.SetConsoleLogger()

//メインループスケジューリンググループを宣言します

var dispatcher = new DispatcherEventLoopGroup()

/ *

Nettyは、さまざまな転送を処理するために、EventLoopGroupのさまざまな実装を提供します。

この例では、サーバー側アプリケーションを実装したため、2つのNioEventLoopGroupが使用されます。

最初のものは、着信接続を受信するために「ボス」と呼ばれることがよくあります。 2つ目は「ワーカー」と呼ばれることが多く、すでに受信されている接続を処理します。 「ボス」は接続を受信すると、接続情報を「ワーカー」に登録します。

使用されたスレッドの数を知る方法、作成済みのチャネルにマップする方法は、IEventLoopGroupの実装によって異なり、コンストラクターを介してそれらの関係を構成できます。

* /

//メインワーカースレッドグループ、1スレッドに設定

IEventLoopGroup bossGroup =ディスパッチャー//(1)

//子ワーカースレッドグループ、1スレッドに設定

IEventLoopGroup workerGroup = new WorkerEventLoopGroup(dispatcher)

試してみてください

{{

//サーバーのブートストラップを宣言します。各NettyサーバープログラムはServerBootstrapによって制御され、必要なパラメーターは連鎖的にアセンブルされます。

var serverBootstrap = new ServerBootstrap()//(2)

//メインスレッドグループとワーカースレッドグループを設定します

serverBootstrap.Group(bossGroup、workerGroup)

if(ServerSettings.UseLibuv)

{{

//サーバー通信チャネルがTcpServerChannelであることを宣言します

serverBootstrap.Channel()//(3)

}

serverBootstrap

//ネットワークIOパラメータなどを設定します。

.Option(ChannelOption.SoBacklog、100)//(5)

//メインスレッドグループにログを出力するようにプロセッサを設定します

.Handler(new LoggingHandler( 'SRV-LSTN'))

//ワーカースレッドパラメーターを設定します

.ChildHandler(

/ *

* ChannelInitializerは、ユーザーが新しいチャネルを構成できるようにすることを目的とした特別な処理クラスです。

* DiscardServerHandlerなどの処理クラスを追加して、新しいチャネルまたはそれに対応するChannelPipelineを構成することにより、ネットワークプログラムを実装したい場合があります。

*プログラムがより複雑になると、piplineに処理クラスを追加してから、それらの匿名クラスを最上位クラスに抽出できます。

* /

new ActionChannelInitializer(//(4)

チャネル=>

{{

/ *

*ワーカースレッドコネクタはパイプで設定され、サーバーのメインスレッドで受信したすべての情報はパイプレイヤーを介して送信されます。

*同時に、ポップされるすべてのメッセージも、このパイプラインのすべてのプロセッサによって段階的に処理されます。

* /

IChannelPipelineパイプライン= channel.Pipeline

//ログインターセプターを追加します

pipe.AddLast(new LoggingHandler( 'SRV-CONN'))

//ポップアップメッセージを追加し、このハンドラーを介してメッセージの長さをメッセージの先頭に追加します。

// LengthFieldPrepender(2):2バイトを使用してデータの長さを格納します。

pipe.AddLast( 'framing-enc'、new LengthFieldPrepender(2))

/ *

プッシュメッセージはハンドラーを渡し、メッセージのパケット長情報を解析して、正しいメッセージ本文を次の処理ハンドラーに送信します。

1、InitialBytesToStrip = 0、//読み取り時にスキップするバイト数

2、LengthAdjustment = -5、/ /パッケージの実際の長さの修正。パケットの長さにヘッダーと本文が含まれている場合は、長さの前の部分を減算します。

3、LengthFieldLength = 4、//長さフィールドのバイト数整数は4バイトです

4、LengthFieldOffset = 1、/ /長さ属性の開始(オフセット)ビット

5、MaxFrameLength = int.MaxValue、//最大パケット長

* /

pipe.AddLast( 'framing-dec'、new LengthFieldBasedFrameDecoder(ushort.MaxValue、0、2、0、2))

//ビジネスハンドラー

pipe.AddLast( 'echo'、new EchoServerHandler())

}))

//指定されたポートへのブートストラップバインディングの動作は、サーバーがサービスを開始し、同じServerbootstrapが複数のポートにバインドできることです。

IChannel boundChannel = await serverBootstrap.BindAsync(ServerSettings.Port)//(6)

Console.WriteLine( 'クライアント入力を待つ')

Console.ReadLine()

//サービスを閉じます

boundChannel.CloseAsync()を待つ

}

最後に

{{

//指定されたワークグループスレッドを解放します

Task.WhenAll(//(7)を待つ

bossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100)、TimeSpan.FromSeconds(1))、

workerGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100)、TimeSpan.FromSeconds(1))

)。

}

}

static void Main()=> RunServerAsync()。Wait()

}

}

  1. IEventLoopGroupは、I / O操作を処理するためのマルチスレッドイベントサーキュレーターです。 DotNettyは、さまざまな転送を処理するために、EventLoopGroupのさまざまな実装を提供します。この例では、サーバー側アプリケーションを実装したため、2つのIEventLoopGroupが使用されます。最初のものは、着信接続を受信するために「ボス」と呼ばれることがよくあります。 2つ目は「ワーカー」と呼ばれることが多く、すでに受信されている接続を処理します。 「ボス」は接続を受信すると、接続情報を「ワーカー」に登録します。

  2. ServerBootstrapは、トランスポートサービスを開始するセカンダリスタートアップクラスです。このサービスではChannelを直接使用できますが、これは複雑なプロセスになる可能性があり、多くの場合、これを行う必要はありません。

  3. ここでは、TcpServerChannelクラスを使用して、新しいチャネルが着信接続を受信する方法を説明するように指定します。

  4. ChannelInitializerは、特別な処理クラスです。その目的は、ユーザーが新しいチャネルを構成できるようにすることです。プログラムがより複雑になると、piplineに処理クラスを追加して、これらの匿名クラスを最大限に抽出することができます。クラスのトップレベル。

  5. ここで指定したチャネル実装の構成パラメーターを設定できます。 TCP / IPサーバーを作成しているため、tcpNoDelayやkeepAliveなどのソケットパラメーターオプションを設定できます。

  6. ポートをバインドしてサービスを開始します。ここでは、マシンのネットワークカードの設定ポートをマシンにバインドします。もちろん、bind()メソッドを複数回呼び出すことができます(異なるバインディングアドレスに基づいて)。

  7. 使用後、指定されたワークグループスレッドをエレガントに解放します。もちろん、プログラムを閉じることもできますが、これはお勧めしません。

サーバー側のイベント処理コード:

コードの前の部分での太字の実装

名前空間Echo.Server

{{

システムを使用する

System.Textを使用する

DotNetty.Buffersを使用する

DotNetty.Transport.Channelsを使用する

///

///サーバーはイベント関数を処理します

///

パブリッククラスEchoServerHandler:ChannelHandlerAdapter // ChannelHandlerAdapterビジネスは基本クラスアダプターを継承します//(1)

{{

///

///パイプラインが読み取りを開始します

///

///

///

public override void ChannelRead(IChannelHandlerContext context、object message)//(2)

{{

if(メッセージはIByteBufferバッファ)//(3)

{{

Console.WriteLine( 'クライアントから受信:' + buffer.ToString(Encoding.UTF8))

}

context.WriteAsync(message)//(4)

}

///

///パイプラインの読み取りが完了しました

///

///

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

///

/// 異常な

///

///

///

public override void ExceptionCaught(IChannelHandlerContext context、Exception exception)

{{

Console.WriteLine( '例外:' +例外)

context.CloseAsync()

}

}

}

  1. DiscardServerHandlerはChannelInboundHandlerAdapterから継承します。このクラスは、IChannelHandlerインターフェイスを実装します。 IChannelHandlerは、イベント処理用の多数のインターフェイスメソッドを提供し、これらのメソッドをオーバーライドできます。ここで、インターフェイスメソッドを自分で実装する代わりに、ChannelInboundHandlerAdapterクラスを拡張する必要があります。

  2. ここでは、chanelRead()イベントハンドラーをオーバーライドします。クライアントから新しいデータを受信するたびに、メッセージを受信したときにこのメソッドが呼び出されます。この例では、受信したメッセージのタイプはByteBufです。

  3. クライアントから送信された情報に応答または表示するために、コンソールでクライアントからのデータを印刷します。

  4. 次に、context.WriteAsyncを介してクライアントからクライアントにメッセージを書き戻します。

  5. もちろん、ステップ4は単にストリームをコンテキストにキャッシュし、実際の書き込み操作を実行しません。フラッシュデータはフラッシュによってパイプラインに書き込まれ、コンテキストを介して着信クライアントに返されます。

クライアント側のコード:

コメントの場所に注目してください。他の場所とサーバー側の間に違いはありません。

名前空間Echo.Client

{{

システムを使用する

System.Netを使用する

System.Textを使用する

System.Threading.Tasksを使用する

DotNetty.Buffersを使用する

DotNetty.Codecsを使用する

DotNetty.Handlers.Loggingを使用する

DotNetty.Transport.Bootstrappingを使用する

DotNetty.Transport.Channelsを使用する

DotNetty.Transport.Channels.Socketsを使用する

Examples.Commonを使用する

静的クラスプログラム

{{

静的非同期タスクRunClientAsync()

{{

ExampleHelper.SetConsoleLogger()

var group = new MultithreadEventLoopGroup()

試してみてください

{{

var bootstrap = new Bootstrap()

ブートストラップ

.Group(group)

。チャネル()

.Option(ChannelOption.TcpNodelay、true)

.Trader(

新しいActionChannelInitializer(

チャネル=>

{{

IChannelPipelineパイプライン= channel.Pipeline

pipe.AddLast(new LoggingHandler())

pipe.AddLast( 'framing-enc'、new LengthFieldPrepender(2))

pipe.AddLast( 'framing-dec'、new LengthFieldBasedFrameDecoder(ushort.MaxValue、0、2、0、2))

pipe.AddLast( 'echo'、new EchoClientHandler())

}))

IChannel clientChannel = await bootstrap.ConnectAsync(new IPEndPoint(ClientSettings.Host、ClientSettings.Port))

// While(true)と同様に、無限ループを作成します

for()//(4)

{{

Console.WriteLine( 'データを入力してください:')

//設定に基づいてバッファサイズを作成します

IByteBuffer initialMessage = Unpooled.Buffer(ClientSettings.Size)//(1)

文字列r = Console.ReadLine()

//データストリームをバッファに書き込みます

initialMessage.WriteBytes(Encoding.UTF8.GetBytes(r ?? throw new InvalidOperationException()))//(2)

//バッファストリームをパイプラインに書き込みます

clientChannel.WriteAndFlushAsync(initialMessage)//(3)を待つ

if(r.Contains( 'bye'))

ブレーク

}

Console.WriteLine( 'byebye')

clientChannel.CloseAsync()を待つ

}

最後に

{{

group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100)、TimeSpan.FromSeconds(1))を待つ

}

}

static void Main()=> RunClientAsync()。Wait()

}

}

  1. バッファのサイズを初期化します。

  2. デフォルトのバッファはbytes []のデータ型を受け入れます。これにより、もちろんストリームへのシリアル化が容易になります。

  3. ストリームダイレクトデータをバッファからチャネルパイプラインに書き込みます。このパイプは通常、リンク通信のもう一方の端(C端)です。

  4. 無限ループが確立されます。これの目的は、サーバーループが1回行われた後、次の入力操作が行われた後、毎回クライアントから入力する必要のあるデータをテストすることです。

クライアント側のイベント処理コード:

名前空間Echo.Client

{{

システムを使用する

System.Textを使用する

DotNetty.Buffersを使用する

DotNetty.Transport.Channelsを使用する

パブリッククラスEchoClientHandler:ChannelHandlerAdapter

{{

読み取り専用IByteBufferinitialMessage

public override void ChannelActive(IChannelHandlerContext context)=> context.WriteAndFlushAsync(this.initialMessage)

public override void ChannelRead(IChannelHandlerContext context、object message)

{{

if(メッセージはIByteBuffer byteBuffer)

{{

Console.WriteLine( 'サーバーから受信:' + byteBuffer.ToString(Encoding.UTF8))

}

}

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

public override void ExceptionCaught(IChannelHandlerContext context、Exception exception)

{{

Console.WriteLine( '例外:' +例外)

context.CloseAsync()

}

}

}

非常にシンプルで、データストリームをコンソールに表示します。

実施結果

この時点で、DotNettyフレームワークを使用して単純な応答サーバーを構築します。これは非常に簡単で、効果は次のとおりです。

CエンドがアクティブにデータをSエンドに送信した後、Sエンドはデータを受信し、コンソールにデータを出力して、Cエンドに送り返します。もちろん、S側は多くのことを行うことができます。

640?wx_fmt = png

DotNetty内部デバッグレコード分析

DotNettyは技術文書を提供していませんが、公式は詳細なデバッグ記録を提供しています。多くの場合、学習者はレコードをデバッグすることで特定の関数の実装プロセスを分析することもできます。 DotNettyの内部入力および出力レコードをコンソールに出力できます。

InternalLoggerFactory.DefaultFactory.AddProvider(new ConsoleLoggerProvider((s, level) => true, false))

サーバー側にはさらに多くの印刷レコードがあり、そのほとんどがDotNetty内部デバッグの印刷レコードであることがわかります。以下のセクションのみに焦点を当てます。

dbug:SRV-LSTN [0]

[id:0x3e8afca1] HANDLER_ADDED

dbug:SRV-LSTN [0]

[id:0x3e8afca1]登録済み(1)

dbug:SRV-LSTN [0]

[id:0x3e8afca1]バインド:0.0.0.0:8007(2)

クライアントの入力を待つ

dbug:SRV-LSTN [0]

[id:0x3e8afca1、0.0.0.0:8007]アクティブ(3)

dbug:SRV-LSTN [0]

[id:0x3e8afca1、0.0.0.0:8007]読む(4)

dbug:SRV-LSTN [0]

[id:0x3e8afca1、0.0.0.0:8007]受信:[id:0x7bac2775、127.0.0.1:64073:> 127.0.0.1:8007](5)

dbug:SRV-LSTN [0]

[id:0x3e8afca1、0.0.0.0:8007] RECEIVED_COMPLETE(6)

dbug:SRV-LSTN [0]

[id:0x3e8afca1、0.0.0.0:8007]読む(7)

dbug:SRV-CONN [0]

[id:0x7bac2775、127.0.0.1:64073 => 127.0.0.1:8007] HANDLER_ADDED(8)

dbug:SRV-CONN [0]

[id:0x7bac2775、127.0.0.1:64073 => 127.0.0.1:8007]登録済み(9)

dbug:SRV-CONN [0]

[id:0x7bac2775、127.0.0.1:64073 => 127.0.0.1:8007]アクティブ(10)

dbug:SRV-CONN [0]

[id:0x7bac2775、127.0.0.1:64073 => 127.0.0.1:8007]読む(11)

dbug:DotNetty.Buffers.AbstractByteBuffer [0](12)

-Dio.netty.buffer.bytebuf.checkAccessible:True

dbug:SRV-CONN [0]

[id:0x7bac2775、127.0.0.1:64073 => 127.0.0.1:8007]受信:14B(13)

+ ------------------------------------------------- +

| 0 1 2 3 4 5 6 7 8 9 a b c d e f |

+ -------- + ---------------------------------------- --------- + ---------------- +

| 100000000 | 00 0C 68 65 6C 6C 6F 20 77 6F 72 6C 64 21 | ..hello world! |

+ -------- + ---------------------------------------- --------- + ---------------- +

クライアントから受信:Hello World!

dbug:SRV-CONN [0](14)

[id:0x7bac2775、127.0.0.1:64073 => 127.0.0.1:8007]書き込み:2B

+ ------------------------------------------------- +

| 0 1 2 3 4 5 6 7 8 9 a b c d e f |

+ -------- + ---------------------------------------- --------- + ---------------- +

| 100000000 | 00 0C | .. |

+ -------- + ---------------------------------------- --------- + ---------------- +

dbug:SRV-CONN [0](15)

[id:0x7bac2775、127.0.0.1:64073 => 127.0.0.1:8007]書き込み:12B

+ ------------------------------------------------- +

| 0 1 2 3 4 5 6 7 8 9 a b c d e f |

+ -------- + ---------------------------------------- --------- + ---------------- +

| 100000000 | 68 65 6C 6C 6F 20 77 6F 72 6C 64 21 | Hello world! |

+ -------- + ---------------------------------------- --------- + ---------------- +

dbug:SRV-CONN [0](16)

[id:0x7bac2775、127.0.0.1:64073 => 127.0.0.1:8007] RECEIVED_COMPLETE

dbug:SRV-CONN [0](17)

[id:0x7bac2775、127.0.0.1:64073 => 127.0.0.1:8007] FLUSH

dbug:SRV-CONN [0](18)

[id:0x7bac2775、127.0.0.1:64073 => 127.0.0.1:8007]読み取り

一見、少し多すぎるように見える18の操作があります。実際には、コンソールに出力されない内部デバッグの詳細がたくさんあります。

  1. ワーカースレッドのセットを手動で作成し、スレッドのセットをパイプラインに登録することにより、このパイプラインはSOCKERに基づくことができ、IChannel(1)に基づくことができます。

  2. カスタムIPアドレスとポート番号をカスタムパイプにバインドします(2)

  3. カスタムパイプラインをアクティブ化する(3)

  4. 読み始める(実際には聞き始める)(4)

  5. ID 0x7bac2775からクライアント接続要求を受信し、接続を確立して、リスニングを開始し続けました(5)(6)(7)

  6. 8番目のステップから、ログはもちろんID 0x7bac2775のレコードになります。これには、登録パイプライン、アクティブ化パイプライン、監視の開始などが含まれます。S端末と同じ操作(8)(9)(10) (11)

  7. 著者が「HelloWorld!」に入ったときdata、DotNetty.Buffers.AbstractByteBufferは、データ型チェックを実行して、データをパイプラインに入れることができることを確認します。 (12)

  8. データをS側に送信します。データサイズは14Bで、hello worldの前に2つのポイントがあります。これは、これがデータヘッダーであることを意味し、さらに2つのポイントを送信しますが、データがないため、データには終了しました。 DotNettyは、データの16進ストレージビットをわかりやすく表現し、非常にユーザーフレンドリーです。 (13)(14)

  9. Sエンドは、処理や処理を行わずにデータを受信し、すぐにCエンドにデータを返します。 (15)(16)

  10. 最後に、このプロセスが完了すると、バッファ領域のデータをパイプラインに強制的に入れる必要があるため、フラッシュ操作が実行され、転送全体が完了します。次に、C側かS側かにかかわらず、状態をREADに変更し続けます。これは、接続ステータスやデータ送信など、パイプラインのさまざまな状況を監視するために使用されます(17)。

総括する

ソケットプログラミングの複雑さはマルチスレッドほど簡単でも複雑でもないため、ソケットプログラミングに不慣れな友人にとってはこれは悪夢です。プロトコル、圧縮、トランスポート、マルチスレッド、スヌーピング、フロー制御などは一連の問題の前にあるため、Nettyは優れたオープンソースフレームワークですが、プロトコルを実装する必要があるため、Nettyは半完成品です。彼に基づいて欲しい。 、転送やその他のカスタム操作、および基になるコンテンツについては、まったく気にしません。 Newtonsoft.Jsonなどの一部のフレームワークとは異なり、機能フレームワーク、構成、カスタマイズは直接使用できません。

DotNettyは、下部に多くの操作を実装するのに役立ちましたが、DotNettyに慣れていない場合、またはネットワーク通信をまったく理解していない場合は、上記のコードについても同じことが言えます。どうして?市場のニーズ、プログラマーは毎日ビジネスに駆けつけています。どうすれば詳細を知り、学ぶことができますか...デバッグレコードを印刷して1つずつ分析することで、私はゆっくりと最も理解し始めます。シンプルなコミュニケーションプロセス。

この記事では、DotNettyに基づく最も単純な通信プロセスのみを実装し、データのループを実行するだけで、RPC関連の呼び出しは行いません。次は、この例について話し始め、DotNettyに基づくRPC呼び出しを紹介します。

元のアドレス:https://www.cnblogs.com/SteveLee/p/9860507.html

転載:https://www.cnblogs.com/lhxsoft/p/10783073.html