【问题标题】:A method for making HTTP requests on Unity iOS?在 Unity iOS 上发出 HTTP 请求的方法?
【发布时间】:2012-08-26 19:37:58
【问题描述】:

我需要使用所有标准 RESTful 方法发送 HTTP 请求并访问请求的主体,以便使用它发送/接收 JSON。我已经调查过了,

WebRequest.HttpWebRequest

这几乎可以完美地工作,但是在某些情况下,例如,如果服务器关闭,则 GetResponse 函数可能需要几秒钟才能返回 - 因为它是同步方法 - 在此期间冻结应用程序。此方法的异步版本 BeginGetResponse 似乎不能异步工作(无论如何在 Unity 中),因为它仍然会在这段时间内冻结应用程序。

UnityEngine.WWW#

出于某种原因仅支持 POST 和 GET 请求 - 但我还需要 PUT 和 DELETE(标准 RESTful 方法),所以我没有再费心去研究它。

System.Threading

为了在不冻结应用程序的情况下运行 WebRequest.HttpWebRequest.GetResponse,我研究过使用线程。线程似乎在编辑器中工作(但似乎非常不稳定 - 如果您在应用程序退出时不停止线程,即使您停止它,它也会永远在编辑器中运行),并且当构建到 iOS 设备时,它会尽快崩溃当我尝试启动一个线程时(我忘记写下错误,我现在无权访问它)。

在原生 iOS 应用中运行线程并连接到 Unity 应用

可笑,甚至不会尝试。

UniWeb

这个。我想知道他们是如何做到的。

这是我正在尝试的 WebRequest.BeginGetResponse 方法的示例,

// The RequestState class passes data across async calls.
public class RequestState
{
   const int BufferSize = 1024;
   public StringBuilder RequestData;
   public byte[] BufferRead;
   public WebRequest Request;
   public Stream ResponseStream;
   // Create Decoder for appropriate enconding type.
   public Decoder StreamDecode = Encoding.UTF8.GetDecoder();

   public RequestState()
   {
      BufferRead = new byte[BufferSize];
      RequestData = new StringBuilder(String.Empty);
      Request = null;
      ResponseStream = null;
   }     
}

public class WebRequester
{
    private void ExecuteRequest()
    {
        RequestState requestState = new RequestState();
        WebRequest request = WebRequest.Create("mysite");
        request.BeginGetResponse(new AsyncCallback(Callback), requestState);
    }

    private void Callback(IAsyncResult ar)
    {
      // Get the RequestState object from the async result.
      RequestState rs = (RequestState) ar.AsyncState;

      // Get the WebRequest from RequestState.
      WebRequest req = rs.Request;

      // Call EndGetResponse, which produces the WebResponse object
      //  that came from the request issued above.
      WebResponse resp = req.EndGetResponse(ar);
    }
}

...基于此:http://msdn.microsoft.com/en-us/library/86wf6409(v=vs.71).aspx

【问题讨论】:

  • 请举例说明您如何使用 BeginGetResponse... 您的代码也可能有问题(一般评论,不确定 Unity 是否对此调用有特殊行为)。
  • 添加了示例。我相信 Unity 使用了 Mono 2.6 的定制版本。
  • 嘿,先生,您的问题显然很混乱,只能由您自己回答,但我在统一和 http 请求方面面临完全相同的问题。而不是线程,我正在尝试coroutines。我不认为线程是最好的路径,但我仍然无法用协程弄清楚。您对此有任何更新吗?你试过我的路吗?你能分享你的解决方案吗?
  • 当在协程中使用时,有问题的方法仍然会冻结应用程序,直到它返回 - 我的理论是该方法冻结了调用它的线程。我认为协程不涉及线程,因此在它们中调用的任何内容都会在主线程上执行,就像在其他任何地方调用它一样冻结应用程序。在线程中调用该方法效果很好,只是要非常小心地手动关闭线程,因为 unity/mono 似乎不会自动完成。
  • 你回答得很快@Ford,但由于某种原因我没有收到通知。我在 WWWLoadLevelAsync 的并行进程中使用协程取得了很大的成功,所以我认为这里也有可能。但现在我相信,正如你所说,它实际上并没有创建自己的线程,所以它实际上只是在同一个线程中进行异步调用......无论如何,如果你弄清楚了,你能与我们分享你的解决方案吗?还是超过 3 页宽,例如您的 msdn 链接?

标签: c# ios multithreading http unity3d


【解决方案1】:

好的,我终于写了my own solution。我们基本上需要一个RequestState、一个Callback Method和一个TimeOut Thread。在这里,我将复制 what was done in UnifyCommunity(现在称为 unity3d wiki)。这是过时的代码,但比那里的要小,所以在这里显示一些东西更方便。现在我已经删除(在 unit3d wiki 中)System.Actionstatic 以提高性能和简单性:

用法

static public ThisClass Instance;
void Awake () {
    Instance = GetComponent<ThisClass>();
}
static private IEnumerator CheckAvailabilityNow () {
    bool foundURL;
    string checkThisURL = "http://www.example.com/index.html";
    yield return Instance.StartCoroutine(
        WebAsync.CheckForMissingURL(checkThisURL, value => foundURL = !value)
        );
    Debug.Log("Does "+ checkThisURL +" exist? "+ foundURL);
}

WebAsync.cs

using System;
using System.IO;
using System.Net;
using System.Threading;
using System.Collections;
using UnityEngine;

/// <summary>
///  The RequestState class passes data across async calls.
/// </summary>
public class RequestState
{
    public WebRequest webRequest;
    public string errorMessage;

    public RequestState ()
    {
        webRequest = null;
        errorMessage = null;
    }
}

public class WebAsync {
    const int TIMEOUT = 10; // seconds

    /// <summary>
    /// If the URLs returns 404 or connection is broken, it's missing. Else, we suppose it's fine.
    /// </summary>
    /// <param name='url'>
    /// A fully formated URL.
    /// </param>
    /// <param name='result'>
    /// This will bring 'true' if 404 or connection broken and 'false' for everything else.
    /// Use it as this, where "value" is a System sintaxe:
    /// value => your-bool-var = value
    /// </param>
    static public IEnumerator CheckForMissingURL (string url, System.Action<bool> result) {
        result(false);

        Uri httpSite = new Uri(url);
        WebRequest webRequest = WebRequest.Create(httpSite);

        // We need no more than HTTP's head
        webRequest.Method = "HEAD";
        RequestState requestState = new RequestState();

        // Put the request into the state object so it can be passed around
        requestState.webRequest = webRequest;

        // Do the actual async call here
        IAsyncResult asyncResult = (IAsyncResult) webRequest.BeginGetResponse(
            new AsyncCallback(RespCallback), requestState);

        // WebRequest timeout won't work in async calls, so we need this instead
        ThreadPool.RegisterWaitForSingleObject(
            asyncResult.AsyncWaitHandle,
            new WaitOrTimerCallback(ScanTimeoutCallback),
            requestState,
            (TIMEOUT *1000), // obviously because this is in miliseconds
            true
            );

        // Wait until the the call is completed
        while (!asyncResult.IsCompleted) { yield return null; }

        // Deal up with the results
        if (requestState.errorMessage != null) {
            if ( requestState.errorMessage.Contains("404") || requestState.errorMessage.Contains("NameResolutionFailure") ) {
                result(true);
            } else {
                Debug.LogWarning("[WebAsync] Error trying to verify if URL '"+ url +"' exists: "+ requestState.errorMessage);
            }
        }
    }

    static private void RespCallback (IAsyncResult asyncResult) {

        RequestState requestState = (RequestState) asyncResult.AsyncState;
        WebRequest webRequest = requestState.webRequest;

        try {
            webRequest.EndGetResponse(asyncResult);
        } catch (WebException webException) {
            requestState.errorMessage = webException.Message;
        }
    }

    static private void ScanTimeoutCallback (object state, bool timedOut)  { 
        if (timedOut)  {
            RequestState requestState = (RequestState)state;
            if (requestState != null) 
                requestState.webRequest.Abort();
        } else {
            RegisteredWaitHandle registeredWaitHandle = (RegisteredWaitHandle)state;
            if (registeredWaitHandle != null)
                registeredWaitHandle.Unregister(null);
        }
    }
}

【讨论】:

  • 所以这在编辑器中工作得很好并且可能是独立的,但是 WebRequest 只能在 iOS 上使用完整的 .NET 2.0(不是子集)并且它看起来禁用所有/大部分剥离,这也使得应用程序方式不幸的是,对我来说很大。
  • @Michael 是的,我还没有测试过,但它一定是真的。将 API Compatibility Level 设置为 .NET 2.0 并将 Stripping Level 设置为 Disabled 以确保这将起作用。
  • 更新:我将 WebRequest 与 .net 2.0 子集一起使用,这在 Unity 5.1.2 中似乎很好。我在 iOS、Android 和 Windows 手机上进行了测试。使用 .NET 子集还将 iOS 上的可执行文件减少了大约 5mb,但这是一个调试版本。
【解决方案2】:

我让线程在 iOS 上工作 - 我相信它是由于幽灵线程或其他原因而崩溃的。重启设备似乎已经修复了崩溃问题,所以我将只使用带有线程的 WebRequest.HttpWebRequest。

【讨论】:

    【解决方案3】:

    有一种异步执行此操作的方法,无需使用 IEnumerator 和 yield return 的东西。查看 eDriven 框架。

    HttpConnector 类:https://github.com/dkozar/eDriven/blob/master/eDriven.Networking/Rpc/Core/HttpConnector.cs

    我一直在使用 JsonFX 和 HttpConnector,例如在这个 WebPlayer 演示中:http://edrivenunity.com/load-images

    没有 PUT 和 DELETE 不是什么大问题,因为所有这些都可以使用 GET 和 POST 完成。例如,我正在使用 Drupal CMS 的 REST 服务成功地进行通信。

    【讨论】:

      【解决方案4】:
      // javascript in the web player not ios, android or desktop you could just run the following code:
      
      var jscall:String;
          jscall="var reqScript = document.createElement('script');";
          jscall+="reqScript.src = 'synchmanager_secure2.jsp?userid="+uid+"&token="+access_token+"&rnd='+Math.random()*777;";
          jscall+="document.body.appendChild(reqScript);";
      Application.ExternalEval(jscall);
      // cs
      string jscall;
          jscall="var reqScript = document.createElement('script');";
          jscall+="reqScript.src = 'synchmanager_secure2.jsp?userid="+uid+"&token="+access_token+"&rnd='+Math.random()*777;";
          jscall+="document.body.appendChild(reqScript);";
          Application.ExternalEval(jscall);
      
      // then update your object using the your return in a function like this
      // json return object always asynch
      function sendMyReturn(args){
           var unity=getUnity();
           unity.SendMessage("object", "function", args );
      }
      sendMyReturn(args);
      

      或者您可以通过 AJAX 函数发送它,出于安全目的预先编写自定义标头 有了这个,您将需要签名的标头和来自服务器的签名请求 我比较喜欢 md5 签名,它们不是那么大

      【讨论】:

        猜你喜欢
        • 2019-11-30
        • 2022-01-17
        • 1970-01-01
        • 1970-01-01
        • 2020-11-06
        • 1970-01-01
        相关资源
        最近更新 更多