Diarkisは、クライアント間の直接通信を実現するP2P機能を備えています。
DiarkisのP2P機能において、Diarkisサーバクラスタは、クライアントデバイスが直接通信するためのSTUNサーバとTURNサーバの両方の役割を果たします。
P2P通信とは
P2P(ピアツーピア)通信は、分散ネットワークアーキテクチャの一種で、デバイス同士の通信においてメッセージを中継するサーバを置かず、デバイス同士が直接メッセージを送受信する形態です。
デバイス同士が直接通信するため(デバイス同士の物理的な距離に注意する必要はありますが)サーバを介した通信よりはレイテンシを抑えることができ、レイテンシを重視するアプリケーションに適した通信方法です。
また、サーバを介さないためサーバ負荷を抑えることができる一方、その分デバイス側の処理が増えることがあります。
今回の記事では、Diarkisが提供するP2P機能を紹介します。P2Pネットワークそのものについては、こちらや、その他P2Pそのものを解説した記事をご覧ください。
P2Pの基本 – STUNサーバ
STUNサーバは「Session Traversal Utilities for NAT」の略で、デバイス同士の直接通信において、通信先を発見するための仲介サービスとして機能します。
デバイスはUDPパケットをSTUNサーバに送信し、STUNサーバにデバイスのパブリックIPアドレスを伝えることで、デバイスがそのIPアドレスでメッセージ受信することを他のデバイスにも伝達できるようにします。通常、デバイスはルータの裏側に存在しており、そのデバイスに割り当てられているIPアドレスと外部から見えるIPアドレスは異なるため、デバイスが自分自身のパブリックIPアドレスを見つけるためにはSTUNサーバが必要になります。
STUNサーバは、各デバイスから送られてくるUDPパケットヘッダを見て、送信元アドレスを取得します。
STUNサーバについては、こちらの記事などにも解説があります。
P2Pの基本 – TURNサーバ
TURNサーバは「Traversal Using Relays around NAT」の略で、TURNサーバを介して他のデバイスにメッセージを中継する機能を持ちます。
STUNはデバイスの置かれた環境や接続先の状況(ルータの設定等)により、失敗することがあるため、そういった場合にはTURNサーバを経由することでデバイス同士のメッセージ送受信を行うことになります。
TURNサーバについては、こちらの記事などにも解説があります。
P2Pの基本 – ホールパンチング
デバイスがSTUNサーバを介してお互いのパブリックIPアドレスを交換することで、交換したデバイス同士で直接通信することができるようになるはずです。
しかしながらルータは通常、未知のソースアドレスからのパケットを受信しないように設定されているため、パブリックIPアドレスを交換した後に相互にパケットを送りあって、ルータから見てそれらの通信相手を既知として扱えるようにする必要があります。
このことを「ホールパンチング」といい、DiarkisのクライアントSDKでは、Connectメソッドを呼んだ際に内部的にこの処理を行います。
ホールパンチングについては、こちらの記事などにも解説があります。
DiarkisのP2P通信でできること
Diarkisのサーバクラスタは、STUNとTURN両方の役割を担い、Diarkisが提供する「マッチメイキング」「フィールド」「ルーム」などの機能をP2P通信を介して利用することができます。
例えば、「マッチメイキング」モジュールは、クライアントデバイスがカスタム属性に基づいて他のクライアントデバイスを検索できるようにします。
「ルーム」や「フィールド」モジュールは、クライアントデバイス間のパブリックアドレス交換を簡単にし、「ルーム」モジュールはTURNサーバのように他のクライアントデバイスにメッセージを中継するのに役立ちます。
DiarkisのクライアントSDKが提供するAPIについてはこちらをご覧ください。
C#による実装サンプル
以下はC# SDKを用いたDiarkisのP2P機能の利用例です。
using Diarkis.Modules;
// instantiate a new P2P instance by passing a diarkis UDP client instance that is CONNECTED to the Diarkis server cluster
P2P peerOne = new P2P(diarkisUDPClient);
// OnReady is raised when P2P hole punching was a success
peerOne.OnReady += (string addr, int port, double delayTime) =>
{
// P2P is now ready!
// string addr is the address of the peer
// int port is the port of the peer
// double delayTime is the time it took to establish the connection with the peer in milliseconds
};
// OnFail is raised when P2P hole punching fails
peerOne.OnFail += (string addr, int port) =>
{
// P2P failed...
// string addr is the address of the would have been peer
// int port is the port of the would have been peer
// fallback to relay communication
};
// OnMessage is raised when P2P message from the peer is received
peerOne.OnMessage += (string addr, int port, byte[] msg) =>
{
// handle the message from the peer
};
// Start P2P communication with the peer at addr and port
// If Connect succeeds, it will raise OnReady event
peerOne.Connect(addr, port);
DiarkisのP2P機能が提供する価値
STUNもTURNも、接続元のデバイスの接続状況を管理する必要がある「ステートフル」なサーバであるため、単純にサーバを追加するようなスケールアウトができません。
また、接続状態を管理する必要があるため、単純にサーバを削除するとSTUNおよびTURNが期待通りに動作しなくなることから、スケールインも容易ではありません。
Diarkisのサーバクラスタは水平分散が可能な設計になっており、接続元のデバイス状態を保持したままスケールアウト・スケールインができるため、スケーラビリティに関する課題を解決することができます。
おわりに
Diarkisは大規模マルチプレーヤー通信のエンジンとして、サーバ側のカスタマイズ性にも優れていますが、今回紹介したようなP2P機能も備えているため、多様なユースケースで活躍できるでしょう。
開発者はDiarkisを活用することで、通信にまつわる面倒事(暗号化、スケーラビリティ等)を気にせず、アプリケーションロジックの開発に集中することができます。