SuperSocketによるネットワーク通信実装
SuperSocketは.NET環境向けの高性能ネットワーク通信フレームワークであり、サーバーおよびクライアントアプリケーションの開発を簡素化します。本記事では、SuperSocketを使用してクライアント側の通信機能を実装する方法について解説します。
SuperSocketのプロトコル実装テンプレート
SuperSocketには、以下のような組み込みのプロトコル実装テンプレートが用意されています:
- TerminatorReceiveFilter (SuperSocket.SocketBase.Protocol.TerminatorReceiveFilter)
終端記号ベースのプロトコル - CountSpliterReceiveFilter (SuperSocket.Facility.Protocol.CountSpliterReceiveFilter)
固定数の区切り文字を持つプロトコル - FixedSizeReceiveFilter (SuperSocket.Facility.Protocol.FixedSizeReceiveFilter)
固定サイズリクエストのプロトコル - BeginEndMarkReceiveFilter (SuperSocket.Facility.Protocol.BeginEndMarkReceiveFilter)
開始・終端マーカーを持つプロトコル - FixedHeaderReceiveFilter (SuperSocket.Facility.Protocol.FixedHeaderReceiveFilter)
固定ヘッダー形式でコンテンツ長を含むプロトコル
プロトコルの選択
今回の実装では、16進数バイトデータを扱い、最初の2バイトがコンテンツ長を示すという要件があるため、FixedHeaderReceiveFilterを使用します。上位マシンはサーバーとして機能するため、SuperSocket.ClientEngine.EasyClientクライアントオブジェクトを利用します。
アーキテクチャ設計
クライアントを実装するには、以下の3つのインターフェース、3つのイベント、3つのクラスを設計する必要があります:
インターフェース:
- サーバーへの接続
- サーバーからの切断
- サーバーへのデータ送信
イベント:
- サーバーからのデータ受信イベント
- 接続成功イベント
- 切断イベント
実装クラス:
- 固定ヘッダープロトコルを使用したデータ解析クラス
- 生データパケット情報を格納するエンティティクラス
- インターフェースとイベントを実装する機能クラス
今後の最適化として、複数サーバーへの接続管理や、存在しないIPアドレスへの接続時のタイムアウト問題の解決(Ping機能)などが考えられます。これらの機能は後続の記事で完全なソリューションとして提供予定です。
実装コード
必要なNuGetパッケージ:
<PackageReference Include="SuperSocket.ClientEngine" Version="0.10.0" />
<PackageReference Include="SuperSocket.ProtoBase" Version="1.7.0.17" />
データパケットエンティティクラス:
/// <summary>
/// データパケット情報クラス
/// </summary>
public class DataPacket : SuperSocket.ProtoBase.IPackageInfo
{
/// <summary>
/// データバッファ
/// </summary>
public byte[] Buffer { get; }
/// <summary>
/// データ長
/// </summary>
public int Size { get; }
/// <summary>
/// クライアントインスタンス
/// </summary>
public SuperSocket.ClientEngine.EasyClient ClientInstance { get; }
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="size">データサイズ</param>
/// <param name="client">クライアントインスタンス</param>
public DataPacket(int size, SuperSocket.ClientEngine.EasyClient client)
{
Size = size;
Buffer = new byte[size];
ClientInstance = client;
}
}
データ解析クラス:
/// <summary>
/// 固定ヘッダーベースのデータフィルター
/// </summary>
public class HeaderBasedFilter : SuperSocket.ProtoBase.FixedHeaderReceiveFilter<DataPacket>
{
private readonly SuperSocket.ClientEngine.EasyClient _clientInstance;
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="client">クライアントインスタンス</param>
public HeaderBasedFilter(SuperSocket.ClientEngine.EasyClient client)
{
_clientInstance = client;
}
/// <summary>
/// ヘッダーサイズを取得
/// </summary>
protected override int GetHeaderBodyLength(byte[] header)
{
// 最初の2バイトをデータ長として解釈
return BitConverter.ToUInt16(header, 0);
}
/// <summary>
/// ヘッダーサイズを返す
/// </summary>
protected override int GetHeaderLength()
{
return 2; // 2バイトのヘッダー
}
/// <summary>
/// データパケットを解析
/// </summary>
protected override DataPacket ResolvePacket(IBufferStream bufferStream)
{
var bodyLength = GetHeaderBodyLength(bufferStream.Buffers);
var packet = new DataPacket(bodyLength, _clientInstance);
bufferStream.Read(packet.Buffer, 0, bodyLength);
return packet;
}
}