接続が切断された場合の調査

目次

オンラインマルチプレイヤーゲームを構築する際には、クライアントとサーバー間の接続が失敗する場合を想定しておく必要があります。

切断の原因は、ソフトウェアまたはハードウェアです。 接続のいずれかのリンクが失敗すると、メッセージの遅延や、欠落、破損が発生し、接続をシャットダウンしなければなりません。

切断が頻繁に起きる場合は、いくつか解決策を試してみてください。

切断の理由

クライアントが全く接続不可能というケースもあります。(サーバー未到達、サーバーアドレス誤り、DNS使用不可、セルフホストサーバーの未開始など)このような場合では、接続の切断ではなく「(初期)接続不良」と考えられます。

クライアントSDKは切断コールバックの用意があり、切断の要因となります。 予期せぬ切断が発生した場合はこれらの調査を行ってください。 主な切断の要因を並べてみました。これらはクライアント側かサーバー側どちらでも発生します。

クライアントによる切断

  • クライアント側のタイムアウト:サーバーからのACKがないもしくは遅すぎることが原因。詳細については「Timeout Disconnect」を参照してください。
  • クライアントソケットの例外 (接続が失われます).
  • 受信時のクライアントの接続不良(バッファフル、接続ロス) 「Traffic Issues and Buffer Full」を参照してください。
  • 送信時のクライアント接続不良(バッファフル、接続ロス)「Traffic Issues and Buffer Full」を参照してください。

サーバーによる切断

  • サーバー側のタイムアウト: クライアントからのACKがないもしくは遅すぎることが原因。詳細ついては「タイムアウトによる切断」を参照してください。
  • サーバーのバッファフル送信(メッセージ過多)。「Traffic Issues and Buffer Full」 を参照してください。
  • ライセンスもしくはサブスクリプションCCUの限界

タイムアウトによる切断

Unlike plain UDP, Photon's reliable UDP protocol establishes a connection between server and clients: Commands within a UDP package have sequence numbers and a flag if they are reliable. If so, the receiving end has to acknowledge the command. Reliable commands are repeated in short intervals until an acknowledgement arrives. If it does not arrive, the connection is timed out.

Both sides monitor this connection independently from their perspective. Both sides have their rules to decide if the other is still available.

If a timeout is detected, a disconnect happens on that side of the connection. As soon as one side thinks the other side does not respond anymore, no message is sent to it. This is why timeout disconnects are one sided and not synchronous.

タイムアウトによる切断は「まったく」接続できない問題を除けば、もっとも多く発生する問題です。

頻繁にタイムアウトが発生する場合、障害の発生箇所は1つではありません。問題の原因として考えられるシナリオや、修正方法は複数あります。

簡単なチェックリストを以下に用意しました。

  • 送信するデータ量を確認します。 データ量が急増したり、メッセージもしくはメッセージ/秒レートが非常に高くなっている場合、接続の質に関わります。 「送信量を減らす」を参照してください。
  • 他のハードウェアやネットワーク上でも同じ問題が再度発生するかどうか確認します。 「"別の接続を試す"」を参照してください。
  • 再送信する数とタイミングを調整します。「再送信の調整」を参照してください。

  • モバイルアプリケーションを制作している場合、Mobile Background Appsを参照してください。

  • ブレイクポイントを使用してゲームのデバッグを行う場合、こちらに目を通してください。

トラフィックの問題とバッファフル

通常Photonサーバーおよびクライアントはパッケージに実際に置かれてインターネット経由で送信される前に複数のコマンドをバッファします。 バッファフルの問題は、「メモリ不足」の問題に似ています。 Photon Serverとクライアントは、実際にパッケージされインターネット経由で送信される前に、通常はいくつかのコマンドをバッファします。 これにより複数コマンドを(より少ない)パッケージに集約できるようになります。

いずれかの側が大量のコマンドを発生した場合(たとえば、大きなイベントを大量に送信するなど)、バッファ不足になる可能性があります。

バッファを充填すると、さらにラグが生じる原因になります: イベントが反対側に受信されるまでに、通常より長く時間がかかることがわかります。 また、オペレーションのレスポンスが通常よりも遅くなります。

"送信量を減らす"を参照してください。

緊急処置

ログを確認する

これは、最初に確認すべき事項です。

すべてのクライアントには、内部ステートの変化や問題についてのログメッセージを提供する、なんらかのコールバックがあります。 これらのメッセージのログを作成し、問題が発生した場合にはログにアクセスしてください。

有用な情報が表示されない場合には、ロギングを増加することができます。 ロギングを増加させる方法は、APIリファレンスを確認してください。

サーバーをカスタマイズしている場合には、そのサーバーのログを確認してください。

SupportLoggerを有効化する

The SupportLogger is a tool which will log the most commonly needed info to debug problems with Photon, like the (abbreviated) AppId, version, region, server IPs and some callbacks.

For Unity, the SupportLogger is a MonoBehaviour. When not using PUN, you can add this component to any GameObject and set the LoadBalancingClient for it. Call DontDestroyOnLoad() for that GameObject.

Outside of Unity, the SupportLogger is a regular class. Instantiate it and set the LoadBalancingClient, to make it register for callbacks. The Debug.Log method(s) get mapped to System.Diagnostics.Debug respectively.

別のプロジェクトを使用

PhotonのすべてのクライアントSDKには、複数のデモが含まれています。 これらの中から、対象となるプラットロームのデモを使用してください。 デモが失敗する場合には、接続に問題が生じている可能性があります。

別のサーバーまたはリージョンを使用

Photon Cloudを使用している場合には、別のリージョンを簡単に使用できます。

独自にホスティングをおこなう際は、仮想マシンではなく物理マシンを使用しましょう。 サーバーに近いクライアント(ただし同一マシン上やネットワーク上ではなく)で最小ラグ(ラウンドトリップタイム)をテストしましょう。 顧客の近くにサーバーを追加する事も検討してみてください。

別の接続を使用

特定のハードウェアが原因で接続に失敗する場合があります。 他のWifiやルーターなどを試してみてください。 別のデバイスで作動状況が改善するか確認してください。

代替のポート番号を使用

2018年初めからPhoton Cloudのすべての実装で、新しいポート番号の範囲をサポートしています: 新しいポート番号の範囲は5055から5058を使用せず、27000から開始します。

ポートを変更しても違いがないように思えるかもしれませんが、実際には非常に効果的です。 現時点では、とても高い評価のフィードバックをいただいています。

一部のクライアントSDKでは、サーバーから受信するアドレス文字列の番号を置換する必要があります。 ネームサーバーは27000 (旧5058)、マスターサーバーは27001 (旧5055)、ゲームサーバーは27002 (旧5056)へと変更されました。 文字列を置換すれば設定が完了します。

CRC Checkを有効化

クライアントとサーバー間でパッケージが破損する場合があります。 ルーターまたはネットワークが非常にビジーな状態の場合に、こうした破損が生じる可能性が高まります。 またハードウェアやソフトウェアによってはバグが多く、破損が発生する可能性が十分にあります。

PhotonにはオプションでパッケージごとのCRC Checkがあります。 パフォーマンス上の問題が生じるため、デフォルトでは有効化されていません。

この機能を使用するには、クライアントでCRC Checkを有効化してください。 ただし有効化した場合にはサーバーもCRCを送信します。

loadBalancingClient.loadBalancingPeer.CrcEnabled = true

Photonクライアントは、CRC Checkの有効化によって何個のパッケージが欠落したかをトラッキングします。

以下を確認します。

LoadBalancingPeer.PacketLossByCrc

微調整

トラフィックの統計を確認する

一部のクライアントプラットフォームでは、Photonで直接Traffic Statisticsを有効化できます。 トラフィックの統計では、様々なパフォーマンスインジケーターがトラッキングされ、ログを容易に作成できます。

トラフィックの統計は、C#ではLoadBalancingPeerクラス内のTrafficStatsGameLevelプロパティとして利用可能です。 このプロパティによって興味深い値を参照できます。

たとえば、TrafficStatsGameLevel.LongestDeltaBetweenDispatchingを使用して連続するDispatchIncomginCommandsコール間の最長の間隔を確認してください。 もしこの間隔が数ミリ秒以上ならば、ローカルラグが発生している可能性があります。 LongestDeltaBetweenSendingを参照して、クライアントが頻繁に送信しているか確認してください。

TrafficStatsIncomingプロパティとTrafficStatsOutgoingプロパティは、送受信されるバイトやコマンド、パッケージについてより詳細な統計を提供します。

再送信を微調整

C#/.Net Photonライブラリには再送信のタイミングを微調整する2つのプロパティがあります。

PhotonPeer.QuickResendAttempts

LoadBalancingPeer.QuickResendAttemptsは受信側が確認できない高信頼性コマンドを速く繰り返します。結果として、いくつかのメッセージが欠落した場合にトラフィックが少し増加し、遅延が短くなります。

PhotonPeer.SentCountAllowance

Photonクライアントはリライアブルなコマンドをデフォルトで最大6回送信します。 5回目の再送信が終わってもACKがない場合は接続が落とされます。

LoadBalancingPeer.SentCountAllowanceはクライアントが個別の高信頼性メッセージをどれくらいの頻度で繰り返すかを定義します。 クライアントが速く繰り返すならば、LoadBalancingPeer.SentCountAllowanceもより高い頻度で繰り返す必要があります。

QuickResendAttemptsを3に、SentCountAllowanceを7に設定することで状況が改善する場合があります。

繰り返しを増やすことで接続状況が慶全されるとはkぎりません。また、遅延がより長くなる可能性があります。

再送信された高信頼性コマンドを確認する

ResentReliableCommandsの監視を開始する必要があります。

このカウンターは高信頼性コマンドが送信されるたびに増加します(サーバーからの確認が時間内に受信されなかったため)。

LoadBalancingPeer.ResentReliableCommands

もしこの値が上限を超えると接続は不安定になり、パケットは正常に送信されません(どちらの方向にも)。

送信量を減らす

トラフィックの問題を避けるには、送信量を減らします。 送信量を減らすには、いくつかの方法があります:

必要以上に送信しない

必要な分だけ通信してください。 関連する値のみを送信し、それらの値から出来る限り派生させてください。

状況に応じて、送信するものを最適化してください。 何を送信するべきか、また送信頻度を考慮するようにしましょう。 重要でないデータは同期によって強制的に再計算される場合を除き、同期されたデータ、またはゲームの進行内容にもとづいて受信側で再計算されるべきではありません。

例:

  • RTSでは、発生時に複数のユニット向けに「オーダー」を送信できます。 これは1秒間に10回の頻度で各ユニットに位置、回転、速度を送信するよりもはるかに効率的です。

    1500 archersを参照してください。

  • シューティングゲームでは、発射は位置と方向として送信してください。 銃弾は通常、直線で飛びます。このため、100ミリ秒ごとにそれぞれの位置を送信する必要はありません。 銃弾が何かに当たった場合、または銃弾が「非常に多くの」ユニットを通過した後には、銃弾をクリーンアップすることができます。

  • アニメーションは送信しないでください。通常は、プレイヤーの入力やアクションからすべてのアニメーションを派生することができます。 アニメーションを送信すると遅延が発生する可能性が十分にあり、プレイが遅延すると非常に不自然な印象を与えます。

  • デルタ圧縮を使用してください。前回の送信から変更があった値のみを送信してください。 受信側での値を平滑化するため、データ補間を使用してください。 この方法は無理に同期をおこなうよりも望ましく、トラフィックを軽減します。

送信量を抑える

やり取りする型とデータ構造を最適化してください。

例:

  • 小さな整数の場合には、intではなくbyteを使用してください。可能であれば、floatではなくintを使用してください。
  • stringのやり取りを避け、なるべくenumやbyteを使用してください。
  • 送信されるものが明確でない限り、カスタムの型をやり取りしないでください。

静的なデータ、またはよりサイズの大きなデータをダウンロードするには、別のサービスを使用してください(例:マップ)。

Photonはコンテンツ配信システムとして構築されていません。 HTTPベースのコンテンツシステムを使用したほうがコストを抑えられ、管理も容易です。 最大転送単位(MTU)よりも大きなものはすべて分割され、複数の信頼性の高いパッケージとして送信されます(完全なメッセージへと再構成する必要があります)。

送信頻度を抑える

  • 送信レートを下げてください。できれば、10未満に下げることを推奨します。 この設定は、当然のことながらゲームプレイに応じて異なります。 この変更はトラフィックに大きく影響します。

ユーザーのアクティビティや、やり取りするデータにもとづいて、適応送信レートや動的送信レートを使用できます。この設定もトラフィックに大きく影響します。

  • 可能な場合には信頼性を低くして送信してください。 新たなアップデートをすぐに送信する必要がある場合、通常は信頼性の低いメッセージを使用できます。 信頼性の低いメッセージは、繰り返しを発生しません。 例:FPSでは、通常プレイヤーの位置は信頼性を低くして送信されます。

より低いMTUを試す

クライアント側で設定すれば、通常よりも最大パッケージサイズを小さくしてサーバーやクライアントで使用することができます。 MTUを低くするとメッセージ送信の際により多くのパッケージが必要ですが、他の方法でも改善しない場合にはこの方法を試してみてください。

この方法の結果について弊社では確認できていませんので、状況が改善したかどうかぜひお知らせください。

loadBalancingClient.loadBalancingPeer.MaximumTransferUnit = 520;

ツール

Wireshark

このネットワークプロトコルアナライザーおよびロガーは、ゲームのネットワークレイヤーの状況を把握するのに非常に有用です。 このツールを使用すると、ネットワークの観点から詳細な状況が分かります。

Wiresharkは複雑そうに思えますが、実際にはゲームのトラフィックのログは非常に簡単な設定で取得できます。

インストールして起動します。 最初のツールバーアイコンから(ネットワーク)インターフェースのリストを開きます。

PhotonServerSettings in Inspector
Wiresharkツールバー

トラフィックのあるインターフェースの横のボックスにチェックマークをつけます。 不明な場合には、複数のインターフェースのログを取得してください。 次に「Options」をクリックします。

PhotonServerSettings in Inspector
Wireshark - Capture Interfaces

すべてのネットワークトラフィックは必要ではないため、チェックマークをつけたインターフェースごとにフィルタを設定する必要があります。 次のダイアログボックス(「Capture Options」)で、チェックマークをつけたインターフェースをダブルクリックします。 「Interface Settings」ダイアログボックスが開きます。 ここでフィルタを設定できます。

PhotonServerSettings in Inspector
Wireshark - Interface Settings

Photonに関するログを取得するフィルタは以下の通りです:

(udp || tcp) && (port 5055 || port 5056 || port 5057 || port 5058 || port 843 || port 943 || port 4530 || port 4531 || port 4532 || port 4533 || port 9090 || port 9091 || port 9092 || port 9093 || port 19090 || port 19091 || port 19093 || port 27000 || port 27001 || port 27002)

「Start」を押すと、接続後にロギングが開始されます。 問題を再現できたらロギングを停止し(ツールバーの3番目のボタン)保存します。

何をおこなったのか、そしてエラーが定期的に発生する場合には発生頻度や発生日時(ログにはタイムスタンプがあります)も記載してください。 クライアントソースログも添付してください。

..pcapやその他のファイルをメールで送信いただければ、弊社で調査をおこないます。

プラットフォーム固有情報

Unity

PUNはインターバルでServiceコールを実装しています

ただし、Unityではシーンおよびアセットのローディング中やスタンドアロンのプレイヤーウィンドウをドラッグしている間はUpdateが呼ばれません。

シーンのローディング中も接続を維持するには、PhotonNetwork.IsMessageQueueRunning = falseを設定する必要があります。

メッセージキューを一時停止することによる2つの効果:

  • バックグラウンドスレッドはSendOutgoingCommandsを呼び出し、Updateは呼び出されません。 これにより、イベントや操作(RPCまたは同期アップデート)は送信されず肯定応答のみが送信され、接続が維持されます。 受信中のデータはこのスレッドによって実行されません。
  • 受信中のアップデートは全てキューされます。RPCは呼び出されず、アップデートされたオブジェクトの監視もされません。 レベルを変更している間、メッセージキューを一時停止していると前のレベルでのRPC呼び出しを防ぎます。

Photon Unity SDKを使用している場合、おそらくMonoBehaviour UpdateメソッドでServiceコールを行うことになります。

シーンのローディング中、PhotonクライアントのSendOutgoingCommandsが確実に呼ばれるようにするため、バックグラウンドスレッドを実装します。 このスレッドが一時停止するのは、各呼び出し間で100~200ミリ秒なので、パフォーマンスがすべて失われることはありません。

 ドキュメントのトップへ戻る