728x90
Dictionary<string, string> requestData = new Dictionary<string, string>(packet.data);
UnityWebRequest webRequest = UnityWebRequest.Post(url, requestData);

주로 POST 요청을 보낼 때, 같은 UnityWebRequest 객체를 재사용하거나, 내부적으로 데이터를 다시 읽으려고 하는데 실패했을 때 발생하는 에러

while(RequestQueue.Count > 0)
{
	...
    ...
    UnityWebRequest webRequest = UnityWebRequest.Post(url, packet.data);
}

이렇게 쓰면 packet.data가 새 객체가 되어서 요청 가능한 상태가 되는 줄 알았는데 아니였음.
저 한줄에서 wepRequest는 새 객체가 된것이 맞으나 요청할때는 재사용으로 들어가버림.

 

UnityWebRequest.Post(url, packet.data) 이걸 반복 호출하는데 packet.data가 WWWForm 같은 stateful 객체면, 내부 스트림이 이미 닫혀서 "rewind"가 안 되는 것이된다. → Curl error 65

UnityWebRequest.Post(url, packet.data)
이거는 내부적으로 packet.data를 바탕으로 UploadHandler를 만들어서 stream으로 보낸 후, 그 stream을 재사용하려고 하면서 터진다.

 

 

결론

=> packet.data를 매 요청마다 새로 만든다. +(using문으로 개선.)

A. Dictionary<string, string> 인 경우.

var originalData = packet.data;
var dataCopy = new Dictionary<string, string>(originalData);

using (UnityWebRequest webRequest = UnityWebRequest.Post(url, dataCopy))
{
	yield return webRequest.SendWebRequest();
}

 

B. byte[] 인 경우.

var originalData = packet.data;
var dataCopy = new byte[originalData.Length];
Buffer.BlockCopy(originalData, 0, dataCopy, 0, originalData.Length);

using (UnityWebRequest webRequest = new UnityWebRequest(url, "POST"))
{
    webRequest.uploadHandler = new UploadHandlerRaw(dataCopy);
    webRequest.downloadHandler = new DownloadHandlerBuffer();
    webRequest.SetRequestHeader("Content-Type", "application/json");

    yield return webRequest.SendWebRequest();
    // 나머지 처리...
}

 

C. WWWForm인 경우

WWWForm CreateForm(Dictionary<string, string> data)
{
    var form = new WWWForm();
    foreach (var pair in data)
        form.AddField(pair.Key, pair.Value);
    return form;
}
while (attempt < 3 && !requestSucceeded)
{
    var form = CreateForm(packet.data);

    UnityWebRequest webRequest = UnityWebRequest.Post(url, form);
    Debug.Log($"Request :: {url}\n{packet}");

    yield return webRequest.SendWebRequest();
 }

Curl error 65 는 주기적으로 같은 요청을 반복할때 주로 난다고 보면됨.

운영에 관여하는 부분이 아니라면 예외처리 해두어서 그냥 넘겨도 괜찮은듯.

(짜피 주기적으로 계속 요청하는데 30번에 1번꼴로 발생하는거라서 팝업을 띄우는게 아닌이상 플레이에 지장이 없음)

 

+서버가 Json 형식으로 받지 못할수 있음

=> 기존 방식인 UnityWebRequest.Post(url, packet.data) + WWWForm 방식으로 유지하되, rewind(재사용)안되는 방식으로 수정할것. (대부분의 PHP/ASP 서버는 폼타입으로 받음)

 

+최종 결론 (해결함)

원인은

RequestQueue.Enqueue(Instance.prevPacket); 
StartCoroutine(Instance.WaitforRequest());

이렇게 사용했기 때문이고 이유는 isNetworking 플래그가 true로 바뀌기 전에 WaitforRequest가 두번 실행되는 에러로 동시에 같은 packet.data를 사용했기때문에 간헐적으로 rewind 에러가 나온것이다.

중복 호출 방어코드로 수정

    RequestQueue.Enqueue(packet);

    // 이미 돌고 있으면 새로 안 돌림
    if (requestRoutine == null)
        requestRoutine = StartCoroutine(WaitforRequest());
    var data = new Dictionary<string, string>(packet.data);
    byte[] postData = EncodeForm(data); //rewind 방지
    using (UnityWebRequest webRequest = new UnityWebRequest(url, "POST"))
    {
        webRequest.uploadHandler = new UploadHandlerRaw((byte[])postData.Clone());
        webRequest.downloadHandler = new DownloadHandlerBuffer();
        webRequest.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");

        Debug.Log($"Request :: {url}\n{packet}");

        yield return webRequest.SendWebRequest();
        //내용
     }
private static byte[] EncodeForm(Dictionary<string, string> form)
{
    string body = string.Join("&", form.Select(kv => $"{UnityWebRequest.EscapeURL(kv.Key)}={UnityWebRequest.EscapeURL(kv.Value)}"));
    return Encoding.UTF8.GetBytes(body);
}
728x90
Posted by 바르마스
,