FiveHeartsの技術記事
実用的なUnityWebRequestのtimeoutの実装方法
ゲーム開発において、UnityWebRequestにて通信を行う場面は
多々あると思います。
一般的には以下のように、UnityWebRequestに定義されている
timeoutを使えば良いのですが、
実際に商業ベースにのせるとなるとこれでは不完全です。
//3秒でタイムアウト
unityWebRequest.timeout = 3;
まずなぜ不完全なのかを解説します。
それはこのtimeoutがiOS環境においては
必ずしも発火しないためです。
timeoutの判定が取られるということは
サーバーとの通信に失敗した時ですが、
その時の約5%くらいでtimeoutが発火しないのです。
つまり全体で言うと発生確率はかなり低いのですが、
この現象が発生した場合、通信におけるエラーハンドリングを抜けてきます。
当然これはバグの原因になります。
そのため以下のように実装してこれを回避します
public struct Result
{
public bool isTimeoutRetry; //タイムアウトでリトライするときのフラグ
public void Timeout()
{
isTimeoutRetry = true;
}
}
public void Send<T>(ref T request, Action<Result> cb)
{
// リクエストオブジェクトを JSON に変換(byte配列)
reqJson = JsonUtility.ToJson(request);
// (中略)
byte[] dataBytes = System.Text.Encoding.UTF8.GetBytes(reqJson);
// HTTP(POST)通信
CoroutineHandler.StartStaticCoroutine(onSend(url, dataBytes, cb));
}
private IEnumerator onSend(string url, byte[] postData, Action<Result> cb)
{
//リクエスト送信
var req = new UnityWebRequest(url, "POST");
float addTime = 0f;//タイムアウト監視用
AsyncOperation op = req.SendWebRequest();
while (true)
{
if (op.isDone == false)
{
//通信中
yield return null;
addTime += Time.deltaTime;
if (addTime >= 3)
{
//3秒経過したらループを抜ける
//通信を切断する
req.Abort();//通信切断
req.Dispose();//通信のメモリのリリース
Result result = new Result();
Debug.Log("サーバとの通信に失敗");
result.Timeout();
cb(result);
yield break;
}
}
else
{
//通信完了でループを抜ける
break;
}
}
}
重要なのはonSend関数のタイムアウト処理です。
その他の関数や定義については
公開できる範囲に限定して省略して書いています。
実際にこのまま商用ベースにのせれるものではありませんので
参考にはなると思いますがご注意ください。
通信を行う際に呼ばれるのはSend関数です。
CoroutineHandler.StartStaticCoroutine
この部分はMonoBehaviourを継承せずにコルーチンを
使うための独自クラスのstaticな関数です。
onSend関数ではタイムアウトの部分だけを記載しています。
実際に使うにはエラーハンドリングやデータの整合性検証などの
追加実装を行ってください。
今回お伝えしたい点はtimeoutを
そのまま使うのは良くないということです
今回の記事はここまでです。
それではまた別の記事でお会いしましょう。