# p2p\_rudp\_sample

## p2p\_rudp サンプル

### 概要

Room モジュールを使用してユーザー同士が P2P 接続し、RUDP (Reliable UDP) を使用して P2P 通信を行うサンプルです。 Room モジュールの特徴については[こちらのページ](https://help.diarkis.io/diarkis-modules/room)を参照してください。

### ローカル環境でサーバーを起動する

サンプルで使用するサーバーを起動するためのチュートリアルを実施して、ローカル環境で Diarkis サーバーを起動します。

[setup-local-server](https://help.diarkis.io/getting-started/tutorial/setup-local-server "mention")

### サンプルの引数

サンプルの起動時には以下の３つのパラメータを指定してください。

`p2p_rudp_sample.exe serverAddr UID clientKey`

| 引数         | 説明                            |
| ---------- | ----------------------------- |
| serverAddr | Diarkis サーバのエンドポイントを指定してください  |
| UID        | 接続するユーザーの ID を任意の文字列で指定してください |
| clientKey  | クライアントキーを任意の文字列で指定してください      |

#### 起動例：

`p2p_rudp_sample.exe 192.168.1.123:7000 1111 AAAA`

### 起動方法

本サンプルは起動すると Room に接続し、Room のメンバー数が 2 人になるまで待機します。 サンプルプログラムを 2 つ起動することで 2 人のユーザーが Room に参加し P2P 接続の処理が開始します。

### サンプルコード説明

#### Room の P2P を使用する全体的な流れ

1. Diarkis ランタイムおよび Diarkis Module を初期化し、Diarkis サーバーへ接続します。\
   詳細については [Diarkis モジュール利用の全体的な流れ](https://help.diarkis.io/diarkis-module#diarkis-モジュール利用の全体的な流れ) を参照してください。
2. セットアップ\
   Room と P2P モジュールを初期化します。

   ```
   diarkis = Diarkis::DiarkisAllocShared<DiarkisInterface>(uid);
   ...
   // Diarkis Module の Room のセットアップ
   diarkis->SetupRoom();
   // Diarkis Module の P2P のセットアップ
   diarkis->SetupP2P();
   ...
   ```
3. Room へ接続\
   `DiarkisRoomBase::SendCreateOrJoinByCustomID` を使用して Room に接続します。Room に接続することが目的のため `DiarkisRoomBase::SendJoinRandomRoom` 等、他の接続用の API を使用しても問題ありません。

   ```
   auto roomBase = diarkis->GetRoomBase();
   roomBase->SendCreateOrJoinByCustomID("sample_room", 4, 200, uid.c_str(), false);
   ...
   ```
4. P2P 接続を開始\
   Room に接続後、`DiarkisRoomBase::SendStartP2PSync` を実行することで P2P 接続を開始します。この API は Room に接続しているメンバーの中で誰か 1 人が実行すれば他のユーザー含めて全員の P2P 接続処理が開始します。本サンプルでは Room のオーナーとなっているユーザーが代表してこの API を実行しています。

   ```
   // Start P2P
   if (uid == ownerUid)
   {
       roomBase->SendStartP2PSync();
   }
   ```
5. P2P 接続完了待ち\
   本サンプルでは全ユーザーの P2P 接続が完了するまで待機します。 `DiarkisP2PBase::GetConnectedUsers` で P2P 接続が完了しているユーザー数をチェックすることができるため、この人数が想定している人数になるまで待機しています。 `DiarkisP2PBase::GetConnectedUsers` は接続しているピアの数となるため参加人数 - 1 でチェックしています。

   ```
   Diarkis::StdVector<Diarkis::StdString> peerUids = p2pBase->GetConnectedUsers();
   while (peerUids.size() < numClients - 1)
   {
       std::this_thread::sleep_for(std::chrono::milliseconds(30));
       peerUids = p2pBase->GetConnectedUsers();
   }
   ...
   ```
6. P2P で RUDP メッセージを送信\
   P2P での接続完了後、`DiarkisP2PBase::SendBroadcast` を使用して P2P 接続している全ユーザーに対してメッセージを送信しています。 この時 Reliablity に `Reliability::RELIABLE_ORDERED` や `Reliability::RELIABLE_UNORDERED` を指定すると RUDP としてメッセージを送信します。 `Reliability::RELIABLE_ORDERED` では到達保証・順番保証となり `Reliability::RELIABLE_UNORDERED` では到達保証のみとなります。\
   また、RUDP では MTU を超える大きなサイズのパケットも送信することができます。簡単に大量のデータを送ることが出来てしまいますので、ネットワーク負荷を考慮してご使用ください。

   ```
   if (num % 10 == 0)
   {
       reliability = Reliability::RELIABLE_ORDERED;
   }

   // RUDP で大きいサイズ (2000 byte) のメッセージを送信
   if (num % 100 == 0)
   {
       reliability = Reliability::RELIABLE_UNORDERED;
       sendSize = PayloadSize;
   }

   p2pBase->SendBroadcast(buff.data(), sendSize, reliability);
   ...
   ```
7. P2P でメッセージを受信\
   P2P でメッセージを受信すると `DiarkisP2PBase::OnP2PMessage` が発火します。 `DiarkisP2PBase::OnP2PMessage` の引数の `DiarkisMessageEventArgs` では送信元 UID やペイロードなど様々な情報を取得することができます。 ユーザーは `DiarkisP2PBase` を継承し `DiarkisP2PBase::OnP2PMessage` をオーバーライドすることで受信したデータに対してアプリ固有の処理を実装することができます。

   ```
   void DiarkisP2PBase::OnP2PMessage(const DiarkisMessageEventArgs& args)
   {
       ...
   ```
8. 切断処理 `DiarkisP2PBase::Disconnect` を使用して P2P 接続を切断します。このとき、Room に接続したままであれば Room 経由で通信を行うことが可能です。

   ```
   p2pBase->Disconnect();
   ```
9. 終了処理\
   詳細については [Diarkis モジュール利用の全体的な流れ](https://help.diarkis.io/diarkis-module#diarkis-モジュール利用の全体的な流れ) を参照してください。
