RakNetの仕組みを日本語で解説した記事が無かったため勉強ついでにまとめていこうと思います。
RakNetとは
C++で開発されたクロスプラットフォームなゲーム向けネットワークエンジンです。現在は開発が終了しているため基本的にRakNet自体の更新はありません。SLikeNetとして有志の方々が開発を継続していたりします。RakNetはMinecraft統合版やテラリアなどの有名なゲームにも使われています。
特徴
UDPを使用しますが、TCPのようなシーケンス番号や確認応答、コネクション管理、ウィンドウ制御などが実装されているのでUDPの速さをキープしつつ欠点である信頼性を克服しています。使いやすさとパフォーマンスを重視して作られているので手軽にプロジェクトへ導入できます。
※この記事では使い方には触れません。
RakPeer
RakPeerInterface.h
RakPeer.h
RakPeer.cpp
RakNetライブラリのコア的存在です。ほとんどの通信に関する操作はRakPeerを介して行われるでしょう。RakPeerはサーバにもクライアントにもなり得ます。インスタンス化時に最大接続数を指定する必要があるのでRakNetを使用したプロジェクトを考える際は注意です。
RakNetのUDTをベースとしたアーキテクチャなので先にそちらを学んでおくとソースコードも読みやすいと思います。
→ UDTについて軽くまとめました
Packet
// RakNetTypes.h 一部省略しています。
struct Packet
{
SystemAddress systemAddress;
RakNetGUID guid;
unsigned int length;
BitSize_t bitSize;
unsigned char* data;
};
SystemAddress
IPアドレスとポート番号を保持します。RAKNET_SUPPORT_IPV6を指定してコンパイルすることでIPv4とIPv6両方を扱えるようになります。パケット受信時にセットされます。
RakNetGUID
RakNetではピアをIP+PortではなくGUIDで識別します。プロキシのようなネットワークトポロジーの実装を可能にするためです。コネクションが確立されるまでは受信したパケットにはUNASSIGNED_RAKNET_GUIDが設定されます。
Length
dataの長さをbyteで表します。lengthに対しbitSizeではbit単位でdataの長さを表します。通常length == 8 * bitSize
でしょう。
Data
受信したパケットの生データが指定されています。RakPeerは受信したパケットの内容を新しく用意したメモリ領域にコピーするので処理後にメモリの解放が必要になります。解放は簡単です。RakPeerインスタンスのDeallocatePacketにPacketのポインタを渡すだけです。
Priority
PacketPriority.h
パケット送信の際、優先度を設定することができます。
- IMMEDIATE_PRIORITY
- HIGH_PRIORITY
- MEDIUM_PRIORITY
- LOW_PRIORITY
Priorityには以上の4つがあり、送信キューから2の倍数ずつ送信されます。
ex) IMMEDIATE=>8つ、HIGH=>4つ、MEDIUM=>2つ、LOW=>1つ
Reliability
PacketPriority.h
- UNRELIABLE
- UNRELIABLE_SEQUENCED
- RELIABLE
- RELIABLE_ORDERED
- RELIABLE_SEQUENCED
- UNRELIABLE_WITH_ACK_RECEIPT
- RELIABLE_WITH_ACK_RECEIPT
- RELIABLE_ORDERED_WITH_ACK_RECEIPT
UNRELIABLE
UNRELIABLEは単純なUDP通信と同じで信頼性が無く到達確認や再送などもしません。シークエンス番号が重複した際にはパケットを破棄します。
RELIABLE
RELIABLEでは到達確認と再送が行われるので確実にパケットを送信することができます。
必ずパケットは相手へ到達しますが、パケットが前後している可能性があるので注意
SEQUENCED
アプリケーションへ渡すパケットの順番を指定します。
SEQUENCEDではシークエンス番号が新しいものだけを受け付けます。
SequenceNum:1 => SequenceNum:3 => SequenceNum:2
この場合シークエンス番号の2は破棄されます。
ORDERED
アプリケーションへ渡すパケットをシークエンス順にし、必ず連番で渡します。
UNRELIABLEにORDEREDが無いのはこのためです。パケットが必ず届くことが条件です。
*_WITH_ACK__RECEIPT
到達が確認されたことをアプリケーションに通知してほしい際に設定します。例として、再送が行われる際にパケットの内容を最新の情報へ書き換えてから送信することができます。
Ordering Streams
ORDEREDなパケットシークエンスの待機バッファ(チャンネル)が32個用意されています。SEQUENCEDにも最新のシークエンス番号を保持するチャンネルが同じく32個用意されています。
Algorithm for blending ordered and sequenced
ORDEREDなチャンネルにSEQUENCEDな機能を追加するアルゴリズムです。
ORDEREDなチャンネルの最新シークエンス番号をインクリメントすると同時にSEQUENCEDなチャンネルの最新シークエンス番号を0にリセットするだけです。
https://github.com/facebookarchive/RakNet/blob/master/Source/ReliabilityLayer.h#L423-L439
MessageID Types
MessageIdentifiers.h
MessageIDはパケットのタイプを表します。パケットのデータの先頭byteがこれにあたります。パデフォルトのパケットのID一覧はDefaultMessageIDTypesに定義されています。ユーザ定義のパケットを実装する際はID_USER_PACKET_ENUM以降の値を使用してください。
参考資料
http://www.jenkinssoftware.com/
http://www.jenkinssoftware.com/raknet/manual/congestioncontrol.html