ネットワーク その2 パケットロス

ネットワークは信頼できません。

データーパケットを送ったときにはいろいろな事がおこります。

  1. 相手側に届くかもしれない
  2. 届かないかもしれない
  3. 届いたとしても、送った順番とは違う順番で届くかもしれない
  4. 届いたとしても、その内容が壊れてるかもしれないし、誰かによって変更されているかもしれない

4つ目はXNAフレームワークベースのゲームでは問題になりません。XNAフレームワークではLiveのライブラリを使っているので送られるパケットは全て自動的に暗号化されているので、データ自体が改ざんされたり、壊れたりということを気にする必要はありません。

2と3の問題についてはSendDataOptions.ReliableSendDataOptions.InOrderフラグを指定することで解決できます。なぜフラグ指定をしないといけないのか疑問に思う人もいるかもしれません。常にSendDataOptions.ReliableInOrderで送ればいいじゃないか、それ以前になぜ他のネットワークアプリケーションの用にTCPを使わないの?

その理由はゲームでは常にシミュレーション結果をリアルタイムに更新する必要があるからです。TCPやSendDataOptions.ReliableInOrderを指定した場合、以下のような動作をします。

  • 送信側はデータパケットをネットワークに送る
  • 送信側はデータパケットのコピーを内部の送信中キューに保存する
  • パケットが届いた時に、受信側は「データが届いたよ」という確認メッセージを返信をする
  • 送信者は受信側からの確認メッセージが届いたら、そのパケットを送信キューから取り除く
  • 一定時間、確認メッセージが届かなかった場合は、もう一度同じパケットを送信しなおす
  • もしパケットが送信順に届かなかった場合、例えばBとCのパケットがAを受信する前に届いたら、受信側はBとCのパケットをキューに保存する
  • 送信順序を保つために、受信側はAが届くまでの間待たなければいけない

この仕組みはウェブページのHTMLデータ等をダウンロードする時などには有効な方法です。何故なら、AのパケットにはHTMLのヘッダーやレイアウト部分を含んでいるわけですが、その情報無しに残りのデータを表示するのは意味がありません。

ゲームの場合を考えてみましょう。例えばプレイヤーの座標をゲームループ内で送る場合は

  • Aの時間の時のプレイヤー座標を送る
  • Bの時間の時のプレイヤー座標を送る
  • Cの時間の時のプレイヤー座標を送る
  • AとBの座標情報が失われる
  • Cの座標が届く
  • 受信側はCの情報が届いているのにも関わらず、AとBの座標が再び送信するまでの間、待つことになる

なんて間抜けな…。もし受信者がCの時点での座標を知っているのなら、それより過去の時間であるAとBを気にするのは無意味なことです。この状況ではAとBの情報は無視して、最新の座標に更新するのがより自然な方法です。

SendDataOptions.ReliableInOrderを指定したときにパケットロスした場合、レイテンシは増加します。このフラグを指定しない場合にパケットロスが発生した瞬間だけガクっとしますが、その後は問題なくゲームを続けることができます。しかし、フラグを指定した場合、たった一つのパケットロスでさえ、それに続くパケット全てが失ったパケットが再び届くまでの時間分だけ遅延するこになります。

SendDataOptions.ReliableかSendDataOptions.InOrderのどちらかを指定したときには問題にならないことに注意してください。この問題は両方のフラグを指定した(TCPの振舞いと同じ)時のみに発生します。

転送を確実に行いたいが、パケットの送信順を気にする必要が無い(SendDataOptions.Reliableを指定)場合、パケットAがロスしてBが届いた時はゲーム側では単にBを受信したことになり、Aのパケットは送信しなおされますが続くほかのパケットの遅延には影響ありません。

パケットの送信順は気にするが、パケットロスは気にする必要が無い(SendDataOptions.InOrderを指定)場合、それぞれのパケットには番号がつけられ、番号が古いものが、新しいものより後に届いた場合は古いものは無視されます。この方法が最もレイテンシが少ない方法です。

ショーン(Shawn)曰く

  • SendDataOptions.InOrderをできるだけ多く使う
  • SendDataOptions.Reliableは必要なときに使う
  • SendDataOptions.ReliableInOrderはできるだけ少なく

 

原文
http://blogs.msdn.com/shawnhar/archive/2007/12/14/network-packet-loss.aspx