【Unity】通信の話

はじめに

はじめまして。 カヤックのソーシャルゲーム事業部の Unity エンジニアのです。
今回は Unity の通信周りについて紹介します。
この記事はカヤック Unity アドベントカレンダー 2016 の 19 日目の記事です。

HTTP Methods

HTTP(The Hypertext Transfer Protocol)はクライアントとサーバー間の通信のためのプロトコルなんです。このプロトコルでは幾つかの通信メソッドを定義してます。

  • GET
  • POST
  • HEAD
  • PUT
  • DELETE
  • OPTIONS
  • CONNECT

見た目上は多いかもしれないが、実際にゲームを作る時はほとんと GET と POST しか使わないです。

では GET と POST の違いを見てみましょう。

GET POST
キャッシュ できる(こともある) しない(原則)
Encoding type application/x-www-form-urlencoded application/x-www-form-urlencoded 又は multipart/form-data(バイナリー用)
データサイズ制限 ある。URL にデータを載せるので、URL の長さに(2048 キャラクター)制限される ない
安全性 URL にデータを載せるので、パスワードなど秘密情報を送る時に使っちゃいけない GET より安全ですが、より安全性を求めるなら特別な技術が必要(後で紹介します)

複雑に見えますが、これだけ覚えておけば基本大丈夫です:

  • 情報を取得するには GET
  • 情報を送るには POST(特にセンシティブな情報の場合)

URLのエスケープ

リクエストをする時に、パラメータを指定することがよくあります。英数字のパラメータは大丈夫ですが、時には記号や日本語が入ってるパラメータを指定することもあるでしょう。
このようなリクエストも考えると、リクエストをする前に、パラメータをエスケープする必要があります。例えば、foo bar&abcあいうえおfoo%20bar%26abc%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A にしてから、サーバーに送るのです。

エスケープする方法はこのドキュメントを見てみましょう:
Unity Script - WWW.EscapeURL

自前でやってもいいですが、既存ライブラリーもあるので、こだわりがないときは既存のものを使うのがオススメです。

ヘッダ

リクエストとレスポンス本体に関係ない追加情報をつけるため、HTTP のヘッダー(Headers)が使われています。ヘッダーには認証情報や、キャッシュ情報、エンコード、クッキーなどの情報がよく入っています。具体的にはこのページに参考するのがオススメです(英語版のほうがいいです):
MDN - HTTP Headers

改変対策

ゲームではルールでプレイヤー間の公平性を確保のが大事です。改変対策しないと、ユーザーがフェイクなリクエストで他のユーザーデータを操作したり、自動プレイツールでレベルアップしたりすることが可能になりやすいです。ここではよく使う二つの技術を紹介します。

Signature

最も一般的で、有効な方法はリクエストのヘッダーに Signature をつける方法です。流れは大体こんな感じです:

  1. クライアントがリクエストのメソッド、URL、パラメータ、時刻(ここが大事)と他認証に必要な情報を文字列でつないで、BaseString を作る。
  2. BaseString を Encode する。
  3. エンコードされてた BaseString と、秘密な SigningKey を使って、決まったハッシュメソッドで Signature を算出。

細かいことは Twitter の開発サイトを参考にするのをオススメします:
Twitter Dev - Creating a signature

さらに、OAuth という成熟した認証フローがあるので、大体の場合に、OAuth を使うのがオススメです。ドキュメントはこちら:
OAuth

リバースエンジニアリング対策

いずれの Signature 方法でも、最終的に SecretKey というパスワードみたいな文字列が必要となっています。この文字列を手に入れると偽リクエストが作れるので、SecretKey を隠すのが大事です。
文字列はバイナルにコンパイルされても、string コマンドでリストアップできるから、SecretKey を直接にコードに書くではなく、独自なメソッドで組み込んだほうが安全なんです。

チャレンジレスポンス認証

Signature とは別にチャレンジレスポンス認証(Challenge and Response Authentication)という方法もあります。
やり方は Wiki を参考するといいでしょう。

ただ、チャレンジレスポンス認証ではパラメータが認証フローに参加しないので、Proxy などを設けて、パラメータを改竄することができるんです(通称 Man in the middle Attack)。厳密な対策はやはり OAuth などを取り込んだほうがいいです。

ユニーク ID を使ったリトライ

通信のタイムアウトや通信切断時などの後、同じリクエストを送り直すことになりますが、失敗した方のリクエストがサーバに到達していた場合、リトライの通信と合わせて2回サーバで処理が走ることになります。これによって意図せずアイテムが二重で使用されたりする懸念があります。

この対策として、更新系のリクエスト(POST)のとき、GUID などリクエストごとにユニークな ID を発行してリクエストパラメータに追加しておき、通信エラー時は同じ GUID でリトライします。
サーバ側はユーザごとに最後のレスポンスおよび GUID をキャッシュしておき、キャッシュの GUID と同じ GUID のリクエストが送られてきたら、処理を行わずキャッシュされたレスポンスをそのまま返します。
こうしておくことで、通信エラー時にリトライしてもサーバで二重処理されることがなくなります。

CDN

Content delivery network のことを意味しています。Wiki での紹介はこちら

画像、音声、動画など静的なファイルはサイズが大きいし、リクエストごとに変わらないので、アプリサーバーではなく、CDN にホストすることが一般的です。
CDN はファイルを複数のサーバーに置くことで、広範囲、高速度、スケールしやすいなどの特徴があるんです。

世の中では CDN サービスを提供する会社がいっぱいあります。Amazon の CloudFront、MaxCDN、CloudFlare などそれぞれプランがあるので、必要に応じて使うのがいいでしょう。