这个当时再找关于网络的插件,就看到了这个,边看文档便翻译,虽然最后还是没有用过这个插件,但这里备份下,说不定下次就用上了。
BestHttp 文档
介绍
BestHTTP是一个机遇RFC_2616的HTTP/1.1实现,这个支持几乎所有的unity移动端和pc平台。
我的目标是创建一个简单的使用方法,但是也很有用的插件,作为unity插件能够发挥HTTP/1.1的潜力。
这个文档是个简单快速的教学文档,并没有包含所有的函数与属性。
你也可以在包中找到一些 有用的demo:
* Load Image Test:
一个简单的交互式demo,用来演示下载一张图片并用它来作为texture
* Rest API Test:
这个例子演示怎么使用BestHTTP与RestAPI service结合。这个例子用到了json,但BestHTTP本身是不需要任何第三方依赖的。
* Audio Streaming Test:
一个非常简单的例子来演示BestHTTP的流能力
* Stream Leaderboard Test:
另一个流的例子用来演示 慢的带宽(或较慢的server处理)与有效的客户端的缓存。这种方法我们限制用户与server带宽以及server的处理能力。
安装
你需要将plugins文件夹从BestHttp文件夹中移动到assets文件夹。
快速开始:
首先,你需要添加以下代码:
using BestHTTP;
GET requests
向web Server发送一个请求的简单办法就是创建一个 HTTPRequest对象,并向他的构造函数提供url与回调函数。
创建之后,我们质素要调用Send()函数,就发送请求了:
HTTPRequest request = new HTTPRequest(new Uri(“https://google.com”), onRequestFinished);
request.Send();
其中的回调函数 OnRequestFinished()可能的实现为:
void OnRequestFinished(HTTPRequest request, HTTPResponse response)
{
Debug.Log(“Request Finished! Text received: ” + response.DataAsText);
}
就如你看到的,回调函数参数包含一个源request,以及一个从Server获取到的应答数据的HTTPResponse 对象。
如果这个对象为null表示有错误,并且request对象会有一个异常的属性,可以获取额外的关于这个错误的信息。
当请求是在单独的线程中执行,回调函数都是在Unity的主线程调用的,所以我们不需要做线程同步的一些事情。
如果我们想要写更简洁的代码,可以使用lambda语法,在这个例子中,我们可以这么写:
new HTTPRequest(new Uri(“https://google.com”), (request, response) =>
Debug.Log(“Finished!”)).Send();
POST Requests
上面这个例子是GET requsts. 如果我们不需要指定方法,所有的请求都默认是GET Requests。构造函数还有一个参数用来指定请求的方法:
HTTPRequest request = new HTTPRequest(new Uri(“yourserver.com/posturi”),
HTTPMethods.Post,
OnRequestFinished);
request.AddField(“FieldName”, “Field Value”);
request.Send() ;
你可以使用RawData属性来设置字段来POST:
HTTPRequest request = new HTTPRequest(new Uri(“yourserver.com/posturi”),
HTTPMethods.Post,
OnRequestFinished);
request.RawData = Encoding.UTF8.GetBytes(“Field Value”);
request.Send() ;
除了GET,POST之外,你还可以使用HEAD,PUT,DELETE,PATCH:
HEAD:
HTTPRequest request = new HTTPRequest(new Uri(“server.com/posturi”), HTTPMethods.Head,
OnRequestFinished);
request.Send();
PUT:
HTTPRequest request = new HTTPRequest(new Uri(“server.com/posturi”), HTTPMethods.Put,
OnRequestFinished);
request.Send();
DELETE:
HTTPRequest request = new HTTPRequest(new Uri(“server.com/posturi”),
HTTPMethods.Delete,
OnRequestFinished);
request.Send() ;
PATCH:
HTTPRequest request = new HTTPRequest(new Uri(“server.com/posturi”),
HTTPMethods.Patch,
OnRequestFinished);
request.Send();
怎么使用下载数据
大多数时候我们的请求是从server上获取数据。原始的数据可以从HTTPResponse对象的数据属性中访问到。来看个下载image的例子:
new HTTPRequest(new Uri(“http://yourserver.com/path/to/image.png”), (request, response) =>
{
var tex = new Texture2D(0, 0);
tex.LoadImage(response.Data);
guiTexture.texture = tex;
}).Send();
当然有更简单的写作方法:
new HTTPRequest(new Uri(“http://yourserver.com/path/to/image.png”), (request, response) =>
guiTexture.texture = response.DataAsTexture2D).Send();
除了DataAsTexture2D之外还有DataAsText用来转换应答数据为utf8的字符串。在未来可能会有更多的转换类型。
注意:所有的在本文档中的例子没有做错误检测!在你的产品中别忘了做null检测。
从WWW切换
你可以yield一个HTTPRequest通过协同调用:
HTTPRequest request = new HTTPRequest(new Uri(“http :// server.com”));
request.Send();
yield return StartCoroutine (request);
Debug.Log(“Request finished! Downloaded Data:” + request.Response.DataAsText);
Debug.Log的调用只有当request完成之后。这种方式你就可以不必提供一个回调函数了(如果你想你仍然可以使用)。
进阶话题:
这一part将会介绍一些非常有用的用法。
我们可以非常方便的使用HTTPRequest的构造函数来启用或禁用一些基本特性,这些参数如下:
* methodType:
我们想server发送的请求类型,默认的方法类型为 HTTPMethods.Get
* isKeepAlive:
表明我们需要tcp链接,保持与Server的链接,所以连贯的http请求不需要重复链接。默认为true,它可以为我们保留一段时间。否则如果我们知道不再需要requests了,那就可以设置为false.
* disableCache:
告诉BestHTTP使用还是跳过整个缓存机制。如果这个值为true,则系统不会从一个保存的response中查找,也不会保存response.默认是false
身份验证
Best HTTP支持基本的和精华的验证,通过HTTPRequest的Credentials属性:
using BestHTTP.Authentication;
var request = new HTTPRequest(new Uri(“https :// httpbin . org / digest – auth / auth – int / usr / paswd”), (req, resp)
=>
{
if (resp.StatusCode != 401)
Debug.Log(“Authenticated”);
else
Debug.Log(“NOT Authenticated”);
Debug.Log(resp.DataAsText);
5
});
request.Credentials = new Credentials(“usr”, “paswd”);
request.Send()
流
默认情况下,我们提供给HTTPRequest构造函数的回调函数只会调用一次,当server的应答全部下载完成与处理完成的时候。如果用这种方式,我们下载一个很大的文件超出了我们移动设备的内存,则应用就会crash,用户就会开始骂娘。
要避免这种情况,BestHTTP被设计为很简单的控制这种情况:只要设置某个flag为true, 每获取我们规定大小的数据一次就调用一次我们的回调函数。
另外如果我们没有关闭缓存系统,下载的response就会被保存,所以下一次我们可以从本地缓存stream整个response,而不需要修改我们的代码,甚至不需要去接触web Server(注意:server必须发送有效的缓存头(“Expires”header:查看 RFC))。
看个例子:
1: var request = new HTTPRequest(new Uri("http://yourserver.com/bigfile"), (req, resp) =>
2: {
3: List<byte[]> fragments = resp.GetStreamedFragments();
4: // Write out the downloaded data to a file:
5: using (FileStream fs = new FileStream("pathToSave", FileMode.Append))
6: foreach(byte[] data in fragments)
7: fs.Write(data, 0, data.Length);
8: if (resp.IsStreamingFinished)
9: Debug.Log(“Download finished!”);
10: });
11: request.UseStreaming = true;
12: request.StreamFragmentSize = 1 * 1024 * 1024; // 1 megabyte
13: request.DisableCache = true; // already saving to a file, so turn off caching
14: request.Send();
上面这个代码发生了什么:
* 我们打开了flag – UseStreaming, 所以我们的回调函数可能会被调用多次。
* StreamFragmentSize表明我们每次希望接收到的数据的最大长度。
* 当每StreamFragmentSize的chunk下载之后都被调用一次回调函数,直到IsStreamingFinished为true
* 要获取下载的数据,需要使用 GetStreamedFragments()函数。我们需要将他的结果保存到一个临时变量,因为其内部的buffer将会在这次调用之后清空,所以连续调用这个函数的话只会获得null的结果。
* 我们这里禁止了使用缓存,因为我们已经将他保存到文件中并且不想占用太多的空间。
Caching(缓存)
缓存也是基于HTTP/1.1 RFC. 它使用headers来保存与验证response.缓存机制工作于后台,对于它我们仅有的控制权就是来决定启用还是禁用。
如果被缓存的response有“Expires”的header并包含一个未来的日期,BestHTTP就会直接使用缓存的数据,而不需要在经过server的验证。这意味着我们不需要与server进行tcp链接。这可以保存我们的时间,节约带宽,并离线作业。
虽然缓存是自动的,我们还有一些控制的东西,或者我们可以获取一些信息,通过使用HTTPCacheServices类:
* BeginClear():
在一个单独的线程中,开始时清空整个缓存。
* BeginMaitainence():
通过这个函数,我们可以基于最后访问时间进行对缓存的删除。它删除访问时间对于指定的时间较旧的,我们也可以使用这个函数来保证缓存大小在控制之下:
// Delete cache entries that weren’t accessed in the last two weeks, then delete entries to keep the size of
// the cache under 50 megabytes, starting with the oldest.
// 删除缓存中在两周内没有访问的数据,保持缓存大小在50M以下,从最旧的开始
1: HTTPCacheService.BeginMaintainence(new HTTPCacheMaintananceParams(TimeSpan.FromDays(14),50 * 1024 * 1024));
* GetCacheSize():
获取缓存的大小,基于bytes
* GetCacheEntryCount():
获取缓存中保存的条目数量。缓存条目的平均大小可以通过计算获得:
1: float avgSize = GetCacheSize() / (float)GetCacheEntryCount() .
Cookies
控制cookies的操作对于程序员来说是透明的。设置请求cookei头,解析,维护response的Set-Cookie头都在插件中自动完成。
任然有一些控制的途径:
* 可以在每个request中禁用,或者设置全局属性,在HTTPRequest对象的IsCookiesEnabled属性或HTTPManager.IsCookiesEnables属性
* Cookie可以通过调用CookieJar.Clear()函数来删除
* 新的Server发送的cookie,可以通过response的Cookies属性来访问
* 有许多全局设置关于cookies的,可以查看 全局设置 的了解更多。
Proxy(代理)
一个HTTPProxy对象可以设置为一个HTTPRequest的Proxy属性。这种方式将request通过给与的代理:
1: request.Proxy = new HTTPProxy(new Uri("http :// localhost :3128"));
下载进度跟踪
要跟踪与显示下载进度,你可以使用HTTPRequest类的OnProgress事件。这个事件的参数是源HTTPRequest对象,下载的bytes长度,数据的总长度
1: var request = new HTTPRequest(new Uri(address), OnFinished);
2: request.OnProgress = OnDownloadProgress;
3: request.Send();
4: void OnDownloadProgress(HTTPRequest request, int downloaded, int length) {
5: float progressPercent = (downloaded / (length * 100.0f));
6: Debug.Log(“Downloaded: ” + progressPercent.ToString(“F2”) + “%”);
7: }
终止一个Request
你可以通过调用HTTPRequest对象的Abort()函数来终止一个进行中的request:
request = new HTTPRequest(new Uri(“http://yourserver.com/bigfile“), (req, resp) => { … });
request.Send();
// And after some time:
request.Abort();
这时回调函数会被调用,response会是null.
超时
对于一个request你可以设置两个超时:
* ConnectTimeout: 通过属性你可以控制你的APP与Server之间要等待一个连接的时间。默认为20秒
1: request = new HTTPRequest(new Uri("http://yourserver.com/"), (req, resp) => { … });
2: request.ConnectTimeout = TimeSpan.FromSeconds(2);
3: request.Send();
* Timeout: 这个属性可以等待一个request的处理时间(发送一个request,下载一个response的时间)
1: request = new HTTPRequest(new Uri("http://yourserver.com/"), (req, resp) => { … });
2: request.Timeout = TimeSpan.FromSeconds(10);
3: request.Send();
全局设置
通过下面这些属性,我们可以改变默认值,否则就在HTTPRequest的构造函数中指定。所以大部分这些属性都是节约时间的快捷方式。
这些改变将会影响到接下来的所有request的创建。
可以通过HTTPManager类的静态属性来改变默认值:
* MaxConnectionPerServer:
允许连接到一个主机的数量。
http://example.org 与 https://example.org 是两个不同的server。默认值是4
* KeepAliveDefaultValue:
这个值是HTTPRequest.IsKeepAlive的默认值。如果为false,则tcp链接就会在每次request之前i建立,并在之后关闭。如果希望有连续不断的请求最好改为false(应该为true吧?我看错了?)。HTTPRequest构造函数中的值只对这个request管用。默认值为true
* IsCachingDisabled:
这个属性用来启用或禁用缓存服务。HTTPRequest的构造函数中的这个值只对这个request有效。默认值为true
* MaxConnectionIdleTime:
指定BestHTTP的闲置等待时间在结束最后的request销毁链接之前。默认为2分钟
* IsCookiesEnabled:
这个变量可以启用或禁用Cookie操作。默认为true
* CookieJarSize:
可以被操作的Cookie的数量。默认为10485760(10M)
* EnablePrivateBrowsing:
如果这个操作打开则不会再硬盘上写入Cookie.默认为false
* ConnectTimeOut:
HTTPRequest的默认ConnectTimeout值。默认20秒
* RequestTimeOut:
HTTPRequest的默认Timeout值,默认60秒
示例代码:
1: HTTPManager.MaxConnectionPerServer = 10;
2: HTTPManager.RequestTimeout = TimeSpan.FromSeconds(120);
线程安全
因为插件内部是使用线程来平行的处理所有request,所有共享数据(cache,cookie,等等)都被考虑已经实现了线程安全。一些点最好还是要知道一下:
* Request的回调函数的调用,以及其他所有的回调函数(比如WebSocket的回调)都被设计在Unity的主线程(就像unity的事件,比如awake,start,update等等)中调用,所以你不需要做任何线程同步的工作。
创建,发送请求使用多个线程是安全的,但是你需要调用 BestHTTP.HTTPManager.Setup();函数在发送任何请求之前在Unity事件中(比如awake,start)
WebSocket
我们可以通过WebSocket类来使用WebSocket的特性。只需要给WebSocket构造函数传入Server的URL
1: var webSocket = new WebSocket(new Uri(“wss://html5labs-interop.cloudapp.net/echo”));
之后我们可以注册我们的事件监听:
* OnOpen event:
当与Server正式建立连接时候会被调用,这个事件之后WebSocket的IsOpen属性变为true,知道我们或server端关闭连接,或者有错误发生。
1: webSocket.OnOpen += OnWebSocketOpen;
2: private void OnWebSocketOpen(WebSocket webSocket)
3: {
4: Debug.Log("WebSocket Open!");
5: }
* OnMessage event:
从Server接收到一段文本消息时触发。
1: webSocket.OnMessage += OnMessageReceived;
2: private void OnMessageReceived(WebSocket webSocket, string message)
3: {
4: Debug.Log("Text Message received from server: " + message);
5: }
* OnBinary event:
从Server接收到一段二进制数据时触发
1: webSocket.OnBinary += OnBinaryMessageReceived;
2: private void OnBinaryMessageReceived(WebSocket webSocket, byte[] message)
3: {
4: Debug.Log("Binary Message received from server. Length: " + message.Length);
5: }
* OnClosed event:
当我们或Server关闭连接,或一个内部错误发生的时候会触发。
当客户端通过Close函数关闭连接时可以提供一个Code和一条message来标明关闭的原因。Server可以处理这个CODE与Message.
1: webSocket.OnClosed += OnWebSocketClosed;
2: private void OnWebSocketClosed(WebSocket webSocket, UInt16 code, string message)
3: {
4: Debug.Log(“WebSocket Closed!”);
5: }
* OnError event:
触发时机为:不能连接到server,有内部错误发生,连接丢失。
第二个参数是Exception对象,可以为null.这种情况可以通过WebSocket的InternalRequest来获得更多信息。
1: webSocket.OnError += OnError;
2: private void OnError(WebSocket ws, Exception exception)
3: {
4: string errorMsg = string.Empty;
5: if (ws.InternalRequest.Response != null)
6: errorMsg = string.Format("Status Code from Server: {0} and Message: {1}",
7: ws.InternalRequest.Response.StatusCode,
8: ws.InternalRequest.Response.Message);
9: Debug.Log(“An error occured: " + (ex != null ? ex.Message : "Unknown Error " +errorMsg);
10: }
* OnIncompleteFrame event:
查看 Advanced Websocket 那节吧。
当我们注册了时间之后我们就可以打开连接:
1: webSocket.Open();
之后我们就可以获得OnOpen事件,我们就可以给Server发送消息了
1: // Sending out text messages:
2: webSocket.Send(“Message to the Server”);
3: // Sending out binary messages:
4: byte[] buffer = new byte[length];
5: //fill up the buffer with data
6: webSocket.Send(buffer);
所有交互完成之后可以关闭连接:
1: webSocket.Close();
Advanced WebSocket
* Ping messages:
可以启用一个新的线程发送Ping message到server通过设置StartPingThread属性为true在我们接收到OnOpen事件之前。这种方法会定期的向server发送Ping message.间隔可以通过设置PingFrequency属性设置(默认为1000ms)
* Pong messages:
所有从server接收到的Ping message都会被插件自动转换为一个Pong应答
*Streaming:
长文本或二进制消息将会通过片段化获得。这些片段会默认通过插件自动的装配起来,这种机制可以被重写,如果我们注册了OnIncompleteFrame事件的话。这个事件将会在每次客户端接收到不完全的片段的时候调用,这时这些片段将会被插件忽略,不会去组装它们也不会保存它们。这个时间可以用来完成streaming
当前支持的HTTP/1.1特性:
这些特性大部分都是隐藏的或被自动使用的:
● HTTPS
● Store responses in a cache
● Cache validation through the server
● Stream from cache
● Persistent connections
● gzip, deflate content encoding
● Raw and chunked transfer encoding
● Range requests
● Handle redirects
● POST forms and files
● WWW Authentication
FAQ
Q: 我接收到以下错误信息:Internal Compiler error. See the console log for
more information. output was:
Unhandled Exception: System.Reflection.ReflectionTypeLoadException: The classes in the
module cannot be loaded.
A: 检查 安装 那一节的步骤
Q: What Stripping Level can be used with BestHTTP?
A: All stripping levels supported. However there are some cases where you need a link.xml, for
these cases a link.xml is included. You have to copy/merge this xml.
雁过留声,人过留评
学习学习,研究研究,呵呵
拜读一下,哈哈
持续更新,持续来访。
希望你的博客如这个夏天一样火热。