概述
转载至:https://blog.csdn.net/nliki/article/details/98958169
在之前我们讲了Unity原生的WWW方法,现在我们看一看BestHTTP,我们同样用原来的图显示在Unity中。
using UnityEngine;
using BestHTTP;
public class BestHTTPAuthorNliki : MonoBehaviour {
public GameObject Obj;
void Start () {
new HTTPRequest(new System.Uri("http://n.sinaimg.cn/sinacn/w480h306/20180118/6452-fyqtwzu4786357.jpg"), (request, response) =>
{
var tex = new Texture2D(0, 0);
tex.LoadImage(response.Data);
this.Obj.GetComponent<Renderer>().material.mainTexture = tex;
}).Send();
//StartCoroutine(UseWWWLoaderTexture());
}
void Update () {
}
//IEnumerator UseWWWLoaderTexture() {
// var www = new WWW("http://n.sinaimg.cn/sinacn/w480h306/20180118/6452-fyqtwzu4786357.jpg");
// yield return www;
// this.Obj.GetComponent<Renderer>().material.mainTexture = www.texture;
//}
}
运行结果:
在Unity游戏里使用Http协议的情况很常见,原生Unity的WWW提供的功能并不多(支持iPhone上支持 http://, https://和 file://协议。 ftp://协议支持仅限于匿名下载。不支持其他协议。WWW类可用于向服务器发送GET和POST请求。如果提供postData参数,WWW类将默认使用GET和POST)。当我们需要其他方法的时候,可能满足不了我们的需求,这时候就需要我们自己封装Http协议来满足更多的需要。
什么是长连接、短连接?(先了解下HTTP长连接、短连接究竟是什么?)
在HTTP/1.0中默认使用短连接。也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。
而从HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头加入这行代码:Connection:keep-alive
长连接短连接操作过程
短连接
建立连接——数据传输——关闭连接...建立连接——数据传输——关闭连接
长连接
建立连接——数据传输...(保持连接)...数据传输——关闭连接
什么时候用长连接,短连接
长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用建立TCP连接。
短链接一般用于Web网站,因为长连接对于服务端来说会耗费一定的资源,而像Web网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。
短连接弊端和优势
每次发送消息的连接服务器和断开服务器也是一笔不小的开销;
不适合那些大量即时信息的手游;
只在需要同步的时候才和服务器连接发送数据, 减少了服务器的压力;
网络不给力的时候也可以玩,数据发送失败可以手动重发,有限的数据足以保证非WIFI的网络也可以游戏;
(*有兴趣的同学可以了解下网络通信方式)
- -!!! 貌似讲了很多废话,接下来就具体讲一下BestHTTP。
BestHTTP介绍
BestHTTP是一个基于RFC 2616的HTTP/1.1实现,它支持几乎所有的Unity移动和独立平台(参见受支持的平台)。目标是创建一个易于使用,但仍然强大的Unity插件来利用HTTP/1.1的潜力。
首先,在常规使用之后,应该在源文件中添加using语句: using BestHTTP;
向web服务器发出请求的最简单方法是创建一个HTTPRequest对象,为其构造函数提供url和回调函数。在构造了一个新的HTTPRequest对象之后,我们需要做的惟一事情就是使用send()函数实际发送请求。我们来看一个例子:
using UnityEngine;
using BestHTTP;
public class HttpAuthorNliki : MonoBehaviour {
void Start () {
HTTPRequest request = new HTTPRequest(new System.Uri("https://www.nliki.com"), OnRequestFinished);
request.Send();
}
void OnRequestFinished(HTTPRequest request, HTTPResponse response)
{
Debug.Log("Request Finished! Text received: " + response.DataAsText);
}
}
可以看到,回调函数总是接收原始HTTPRequest对象和一个HTTPResponse对象,HTTPResponse对象保存来自服务器的响应。当HTTPResponse请求出现异常时,返回对象为将为null,这个异常属性可能携带有关错误的额外信息。虽然请求总是在单独的线程上处理,但是调用回调函数是在Unity的主线程上完成的,所以我们不需要执行任何线程同步。如果我们想编写更紧凑的代码,我们可以使用c#的lambda表达式。在这个例子中,我们甚至不需要一个临时变量:
new HTTPRequest(new Uri("https://www.nliki.com"), (request, response) => Debug.Log("Finished!")).Send();
POST HEAD, PUT, DELETE和PATCH方法请求
上述的例子是简单的GET请求。如果我们没有指定方法,所有请求默认情况下都是GET请求。构造函数有另一个参数,可以用来指定请求的方法:
● Post
void Start () {
HTTPRequest request = new HTTPRequest(new System.Uri("http://www.nliki.com/path"), HTTPMethods.Post, OnRequestFinished); request.AddField("FieldName", "Field Value");
request.Send();
}
若想要发布任何数据而不设置字段,可以使用RawData属性:
HTTPRequest request = new HTTPRequest(new System.Uri("http://www.nliki.com/path"), HTTPMethods.Post, OnRequestFinished); request.RawData = Encoding.UTF8.GetBytes("Field Value");
request.Send();
除了GET和POST,你还可以使用HEAD, PUT, DELETE和PATCH方法:
● Head
HTTPRequest request =new HTTPRequest(new System.Uri("http://www.nliki.com/path"), HTTPMethods.Head, OnRequestFinished);
request.Send();
● Put
HTTPRequest request = new HTTPRequest(new System.Uri("http://www.nliki.com/path"), HTTPMethods.Put, OnRequestFinished);
request.Send();
● Delete
HTTPRequest request = new HTTPRequest(new System.Uri("http://www.nliki.com/path"), HTTPMethods.Delete, OnRequestFinished);
request.Send();
● Patch
HTTPRequest request = new HTTPRequest(new System.Uri("http://www.nliki.com/path"), HTTPMethods.Patch, OnRequestFinished);
request.Send();
如何下载的资源数据
大多数时候,我们使用请求从服务器接收一些数据。原始字节可以从HTTPResponse对象的数据属性访问。让我们来看一个如何下载图片的例子:
using UnityEngine;
using BestHTTP;
public class HttpAuthorNliki : MonoBehaviour {
public GameObject Obj;
void Start () {
//网上可以随意找一个图片的地址
new HTTPRequest(new System.Uri("http://www.nliki.com/nliki.png"), (request, response) => {
var tex = new Texture2D(0, 0);
tex.LoadImage(response.Data);
this.Obj.GetComponent<Renderer>().material.mainTexture = tex;
}).Send();
}
}
从WWW切换
你可以在StartCoroutine调用的帮助下生成HTTPRequest:
using UnityEngine;
using System.Collections;
using BestHTTP;
public class HttpAuthorNliki : MonoBehaviour
{
void Start()
{
StartCoroutine(UseHTTPRequest());
}
IEnumerator UseHTTPRequest()
{
HTTPRequest request = new HTTPRequest(new System.Uri("http://www.nliki.com"));
request.Send();
//只有当请求完成时才会调用Debug.Log。通过这种方式,您不必提供回调函数(若想也可以提供回调函数)。
yield return StartCoroutine(request);
Debug.Log("Request finished! Downloaded Data:" + request.Response.DataAsText);
}
}
高级用法
预先通知
在HTTPRequest类构造函数的帮助下,我们可以轻松地启用和禁用一些基本特性。这些参数如下:
methodType:我们将向服务器发送什么样的请求。默认的方法类型是HTTPMethods.Get。
isKeepAlive:向服务器表明我们希望tcp连接保持打开状态,因此连续的http请求不需要重新建立连接。如果我们让它默认为true,它可以为我们节省很多时间。如果我们知道我们不会经常使用请求,我们可能会将其设置为false。默认值为true。
disableCache:告诉BestHTTP系统使用或完全跳过缓存机制。如果它的值为true,系统将不会检查缓存中存储的响应,并且响应也不会被保存。默认值为false。
需要引入:using BestHTTP.Authentication;
void Start()
{
var request = new HTTPRequest(new System.Uri("http://www.nliki.com"), (req, resp) => {
if (resp.StatusCode != 401) Debug.Log("Authenticated");
else Debug.Log("NOT Authenticated");
Debug.Log(resp.DataAsText);
});
request.Credentials = new Credentials("usr", "paswd"); request.Send();
request.Send();
}
下载(流)
默认情况下,我们提供给HTTPRequest构造函数的回调函数只会被调用一次,当服务器的答案被完全下载和处理之后。这样,如果我们想下载更大的文件,我们就会很快用完移动设备上的内存。我们的应用会崩溃,使得用户会体验不好。为了避免这种情况,BestHTTP被设计成非常容易地处理这个问题:只要将一个标志切换为true,我们的回调函数就会在每次下载预定义数量的数据时被调用。此外,如果我们没有关闭缓存,下载的响应将被缓存,这样下次我们就可以从本地缓存中传输整个响应,而无需修改代码,甚至无需触及web服务器。(注:服务器必须发送有效的缓存标头(“Expires”标头:参见RFC)来允许这样做。)
var request = new HTTPRequest(new System.Uri("http://yourserver.com/bigfile"), (req, resp) => {
List<byte[]> fragments = resp.GetStreamedFragments();
// Write out the downloaded data to a file:
using (FileStream fs = new FileStream("pathToSave", FileMode.Append))
foreach (byte[] data in fragments) fs.Write(data, 0, data.Length);
if (resp.IsStreamingFinished) Debug.Log("Download finished!");
});
request.UseStreaming = true;
request.StreamFragmentSize = 1 * 1024 * 1024;// 1 megabyte
request.DisableCache = true;// already saving to a file, so turn off caching
request.Send();
我们将标志UseStreaming切换为true,因此我们的回调可能会被调用不止一次。
StreamFragmentSize表示在调用回调之前需要缓冲的最大数据量。
每次下载StreamFragmentSize大小的块时,都会调用回调函数,当IsStreamingFinished设置为true时,会再次调用回调函数。
要获得下载的数据,我们必须使用GetStreamedFragments()函数。我们应该将它的结果保存在一个临时变量中,因为在这个调用中清除了内部缓冲区,所以连续调用将给我们空结果。
我们在本例中禁用了缓存,因为我们已经保存了下载的文件,不想占用太多空间。
更新流)
您可以通过UploadStream属性设置一个流实例来上载到HTTPRequest对象。插件将使用这个流来收集要发送到服务器的数据。当上传完成且Dispose euploadstream为true时,插件将调用流上的Dispose()函数。如果流的长度未知,则应该将UseUploadStreamLength属性设置为false。在这种情况下,插件将使用分块传输编码从流中发送数据。
public class HttpAuthorNliki : MonoBehaviour
{
void Start()
{
var request = new HTTPRequest(new System.Uri("address"), HTTPMethods.Post,OnUploadFinished);
request.UploadStream = new FileStream("File_To.Upload", FileMode.Open); request.Send();
}
void OnUploadFinished(HTTPRequest originalRequest, HTTPResponse response) {
}
}
上载进度跟踪
要跟踪和显示上传进度,可以使用HTTPRequest类的OnUploadProgress事件。OnUploadProgress可以用于原始数据、表单(通过AddField和AddBinaryData)和UploadStream。
public class HttpAuthorNliki : MonoBehaviour
{
void Start()
{
var request = new HTTPRequest(new System.Uri("you.address"), HTTPMethods.Post, OnFinished);
request.RawData = Encoding.UTF8.GetBytes("Field Value");
request.OnUploadProgress = OnUploadProgress;
request.Send();
}
void OnFinished(HTTPRequest originalRequest, HTTPResponse response) {
//TODO:
}
void OnUploadProgress(HTTPRequest request, long uploaded, long length) {
float progressPercent = (uploaded / (float)length) * 100.0f;
Debug.Log("Uploaded: " + progressPercent.ToString("F2") + "%");
}
}
缓冲
缓存也基于HTTP/1.1 RFC。它使用头文件来存储和验证响应。缓存机制在幕后工作,我们唯一要做的就是决定是启用还是禁用它。如果缓存的响应有一个带有未来日期的“Expires”报头,那么BestHTTP将使用缓存的响应,而不与服务器进行验证。这意味着我们不需要启动到服务器的任何tcp连接。这可以节省我们的时间,带宽,以及离线工作。
虽然缓存是自动的,但是我们对它有一些控制,或者我们可以使用HTTPCacheService类的公共函数获得一些信息:
BeginClear():它将开始在一个单独的线程上清除整个缓存。
BeginMaintainence():在这个函数的帮助下,我们可以删除最后一个缓存的条目
GetCacheSize():将以字节为单位返回缓存的大小。
GetCacheEntryCount():将返回缓存中存储的条目数。可以使用float avgSize = GetCacheSize() / (float) GetCacheEntryCount()公式计算平均缓存条目大小。
* 访问时间。它删除最后一次访问时间大于指定时间的条目。我们也可以使用这个函数来控制缓存大小:
// 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.
HTTPCacheService.BeginMaintainence(new HTTPCacheMaintananceParams(TimeSpan.FromDays(14), 50 * 1024 * 1024));
Cookies
cookie操作的处理对程序员来说是透明的。设置请求Cookie头以及解析和维护响应的Set-Cookie头是由插件自动完成的。●通过设置HTTPRequest对象的IsCookiesEnabled属性或HTTPManager,可以对每个请求或全局禁用它。IsCookiesEnabled财产。
可以通过调用cookiejb . clear()函数从CookieJar中删除Cookie。
可以通过响应的cookie属性访问服务器发送的新cookie。
关于cookie有许多全球设置。有关更多信息,请参见全局设置部分
可以将Cookies添加到HTTPRequest中,方法是将它们添加到Cookies列表中:
var request = new HTTPRequest(new System.Uri("address"), OnFinished);
request.Cookies.Add(new Cookie("Name", "Value"));
request.Send();
这些cookie将与服务器发送的cookie合并。如果在请求或HTTPManager中将IsCookiesEnabled设置为false,那么只发送这些用户设置的cookie。
代理(Proxy)
HTTPProxy对象可以设置为HTTPRequest的代理属性。这样请求将通过给定的代理:
request.Proxy = new HTTPProxy(new System.Uri("http://localhost:3128"));
您还可以设置一个全局代理,因此不必手动将其设置为所有请求。
下载进度跟踪
要跟踪和显示下载进度,可以使用HTTPRequest类的OnProgress事件。该事件的参数包括原始HTTPRequest对象、下载的字节和下载内容的预期长度。
public class HttpAuthorNliki : MonoBehaviour
{
void Start()
{
var request = new HTTPRequest(new System.Uri("address"), OnFinished);
request.OnProgress = OnDownloadProgress;
request.Send();
}
void OnFinished(HTTPRequest originalRequest, HTTPResponse response) {
//TODO:
}
void OnDownloadProgress(HTTPRequest request, int downloaded, int downloadLength) {
//TODO:
float progressPercent = (downloaded / (float)length) * 100.0f;
Debug.Log("Downloaded: " + progressPercent.ToString("F2") + "%");
}
}
终止请求 (Aborting a Request )
您可以通过调用HTTPRequest对象的abort()函数来中止正在进行的请求:
request = new HTTPRequest(new System.Uri("http://yourserver.com/bigfile"), (req, resp) => {
//TODO:
});
request.Send();
// And after some time:
request.Abort();
将调用回调函数,并且响应对象为null。
超时(Timeouts )
您可以为一个请求设置两个超时:
ConnectTimeout:使用此属性,您可以控制希望等待应用程序和远程服务器之间连接的时间。它的默认值是20秒
request = new HTTPRequest(new System.Uri("http://yourserver.com/"), (req, resp) => {
//TODO:
});
request.ConnectTimeout = TimeSpan.FromSeconds(2);
request.Send();
Timeout: 使用此属性,您可以控制希望等待处理请求的时间(发送请求和下载响应)。它的默认值是60秒。
request = new HTTPRequest(new System.Uri("http://yourserver.com/"), (req, resp) => {
//TODO:
});
request.Timeout = TimeSpan.FromSeconds(10);
request.Send();
举个简单的例子:
public class HttpAuthorNliki : MonoBehaviour
{
void Start()
{
string url = "http://yourUrl";
HTTPRequest request = new HTTPRequest(new Uri(url), (req, resp) =>{
switch (req.State)
{ // The request finished without any problem.
case HTTPRequestStates.Finished:
Debug.Log("Request Finished Successfully!n" + resp.DataAsText);
break;
// The request finished with an unexpected error. // The request's Exception property may contain more information about the error.
case HTTPRequestStates.Error:
Debug.LogError("Request Finished with Error! " + (req.Exception != null ? (req.Exception.Message + "n" + req.Exception.StackTrace) : "No Exception"));
break;
// The request aborted, initiated by the user.
case HTTPRequestStates.Aborted:
Debug.LogWarning("Request Aborted!");
break;
// Ceonnecting to the server timed out.
case HTTPRequestStates.ConnectionTimedOut:
Debug.LogError("Connection Timed Out!");
break;
// The request didn't finished in the given time.
case HTTPRequestStates.TimedOut:
Debug.LogError("Processing the request Timed Out!");
break;
}
});
// Very little time, for testing purposes:
//request.ConnectTimeout = TimeSpan.FromMilliseconds(2);
request.Timeout = TimeSpan.FromSeconds(5);
request.DisableCache = true; request.Send();
}
}
请求状态(Request States)
所有请求都有一个State属性,该属性包含它的内部状态。可能的状态如下:
初始状态:请求的初始状态。在此状态下不会调用回调。排队:在等待处理的队列中等待。在此状态下不会调用回调。
处理:开始处理请求。在这种状态下,客户机将发送请求并解析响应。在此状态下不会调用回调。
完成:请求顺利完成。解析完响应后,就可以使用结果了。将使用有效的响应对象调用用户定义的回调函数。请求的异常属性将为null。
错误:请求结束时插件出现意外错误。将使用空响应对象调用用户定义的回调函数。请求的Exception属性可能包含关于错误的更多信息,但它可以是null。
中止:客户机(HTTPRequest的Abort()函数)中止的请求。将使用空响应调用用户定义的回调。请求的异常属性将为null。ConnectionTimedOut:连接到服务器超时。将使用空响应调用用户定义的回调。请求的异常属性将为null。
TimedOut:请求没有在给定的时间内完成。将使用空响应调用用户定义的回调。请求的异常属性将为null。有关使用示例,请参阅前一节的示例。
请求优先级(Request Priority )
可以通过HTTPRequest的priority属性更改请求的优先级。优先级较高的请求将比优先级较低的请求更快地从请求队列中选择。
var request = new HTTPRequest(new Uri("https://www.nliki.com"), ...);
request.Priority = -1;
request.Send();
服务器证书验证(Server Certificate Validation )
服务器发送的证书可以通过实现ICertificateVerifyer接口并将其设置为HTTPRequest的CustomCertificateVerifyer来验证:
void Start()
{
string url = "http://yourUrl";
var request = new HTTPRequest(new Uri("https://www.nliki.com"), ...);
request.CustomCertificateVerifyer = new CustomVerifier();
request.UseAlternateSSL = true;
request.Send();
}
using System;
using Org.BouncyCastle.Crypto.Tls;
using Org.BouncyCastle.Asn1.X509;
class CustomVerifier : ICertificateVerifyer {
public bool IsValid(Uri serverUri, X509CertificateStructure[] certs) {
// TODO: Return false, if validation fails
return true;
}
}
重定义(Control Redirections)
重定向由插件自动处理,但有时我们必须在对重定向到的uri发出新请求之前进行更改。我们可以在HTTPRequest的OnBeforeRedirection事件处理程序中执行这些更改。在插件对新uri执行新请求之前调用此事件。函数的返回值将控制重定向:如果为false,重定向将中止。
这个函数是在主线程以外的线程上调用的
void Start()
{
string url = "http://yourUrl";
var request = new HTTPRequest(new Uri(url), HTTPMethods.Post);
request.AddField("field", "data");
request.OnBeforeRedirection += OnBeforeRedirect; request.Send();
}
bool OnBeforeRedirect(HTTPRequest req, HTTPResponse resp, Uri redirectUri)
{
if (req.MethodType == HTTPMethods.Post && resp.StatusCode == 302)
{
req.MethodType = HTTPMethods.Get;
// Don't send more data than needed.
// So we will delete our already processed form data.
req.Clear();
}
return true;
}
}
统计(Statistics)
您可以使用HTTPManager获得关于底层插件的一些统计信息。GetGeneralStatistics功能:
GeneralStatistics stats = HTTPManager.GetGeneralStatistics(StatisticsQueryFlags.All);
Debug.Log(stats.ActiveConnections);
另外三种Statistics方法:
Connections: 将返回基于连接的统计信息。
RequestsInQueue:队列中等待空闲连接的请求数。
Connection:插件跟踪的HTTPConnection实例的数量。这是以下所有连接的和。
ActiveConnections:活动连接的数量。这些连接目前正在处理一个请求。
FreeConnection:免费连接的数量。这些连接使用一个请求完成,它们正在等待另一个请求或回收。
RecycledConnection:可回收连接的数量。这些连接将被尽快删除。
Cache: 基于缓存统计数据:
CacheEntityCount:缓存响应的数量。
CacheSize:缓存响应的大小总和。
Cookie:基于Cookie的统计数据:
CookieCount: 饼干罐里的饼干数量。
CacheSize:饼干罐里饼干的总大小。
整体设置(Global Settings)
使用以下属性,我们可以更改一些默认值,否则应该在HTTPRequest的构造函数中指定这些默认值。所以大多数属性都是节省时间的快捷方式。 这些更改将影响在其值更改后创建的所有请求。可以通过HTTPManager类的静态属性更改默认值:
● MaxConnectionPerServer: 允许到唯一主机的连接数。http://example.org和https://example.org被计算为两个独立的服务器。默认值是4
● KeepAliveDefaultValue:HTTPRequest的IsKeepAlive属性的默认值。如果IsKeepAlive是假的,那么到服务器的tcp连接将在每个请求之前设置,并在请求之后立即关闭。如果连续的请求很少,则应该将其更改为false。给HTTPRequest构造函数的值只会覆盖这个请求的这个值。默认值为true。
● IsCachingDisabled: 使用此属性,我们可以全局禁用或启用缓存服务。给HTTPRequest构造函数的值只会覆盖这个请求的这个值。默认值为true。
● MaxConnectionIdleTime:指定BestHTTP在完成最后一个请求后销毁连接之前应该等待的空闲时间。默认值是2分钟。
● IsCookiesEnabled:使用此选项可以启用或禁用所有Cookie操作。默认值为true。
● CookieJarSize: 使用此选项可以控制Cookie存储的大小。默认值是10485760 (10 MB)。
● EnablePrivateBrowsing: 如果启用此选项,则不会将Cookie写入磁盘。默认值为false。
● ConnectTimeout: 使用这个选项,您可以设置httprequest的默认ConnectTimeout值。默认值是20秒。
● RequestTimeout: 使用这个选项,您可以设置httprequest的默认超时值。默认值是60秒。
● RootCacheFolderProvider:默认情况下,插件将把所有缓存和cookie数据保存在Application.persistentDataPath返回的路径下。您可以将函数分配给此委托,以返回自定义根路径来定义新路径。此委托将在非统一线程上调用!
● Proxy: 所有httprequest的全局缺省代理。HTTPRequest的代理仍然可以根据每个请求进行更改。默认值为null。
● Logger: I Logger实现能够控制关于插件内部的哪些信息将被记录,以及这些信息将如何被记录。
● DefaultCertificateVerifyer: 可以将ICertificateVerifyer实现设置为该属性。当使用安全协议且请求的UseAlternateSSL为真时,在此之后创建的所有新请求都将使用此验证器。ICertificateVerifyer实现可用于实现服务器证书验证
● UseAlternateSSLDefaultValue: HTTPRequest的UseAlternateSSL的默认值可以通过这个属性更改。
//例子
HTTPManager.MaxConnectionPerServer = 10;
HTTPManager.RequestTimeout = TimeSpan.FromSeconds(120);
线程安全(Thread Safety )
因为插件内部使用线程并行处理所有请求,所以所有共享资源(缓存、cookie等)都是考虑到线程安全而设计和实现的。下面是一些值得注意的事项:1调用请求的回调函数,以及所有其他回调(比如WebSocket的回调)都是在Unity的主线程上进行的(比如Unity的事件:awake、start、update等等),所以你不需要做任何线程同步。在多个线程上创建和发送请求也是安全的,但是您应该调用BestHTTP.HTTPManager.Setup();函数的作用,然后从Unity的事件之一发送任何请求(例如:awake, start)。
WebSocket
我们可以通过WebSocket类使用WebSocket特性。我们只需要将服务器的Uri传递给WebSocket的构造函数:
var webSocket = new WebSocket(new Uri("wss://html5......"));
在这一步之后,我们可以注册我们的事件处理程序到几个事件:
● OnOpen事件: 在建立到服务器的连接时调用。在此事件之后,WebSocket的IsOpen属性将为True,直到我们或服务器关闭连接或发生错误。
webSocket.OnOpen += OnWebSocketOpen;
//OnOpen
private void OnWebSocketOpen(WebSocket webSocket)
{
Debug.Log("WebSocket Open!");
}
● OnMessage事件: 当从服务器接收到文本消息时调用。
webSocket.OnOpen += OnMessageReceived;
//OnMessageReceived
private void OnMessageReceived(WebSocket webSocket, string message)
{
Debug.Log("Text Message received from server: " + message);
}
● OnBinary 事件: 当从服务器接收到二进制blob消息时调用。
● OnClosed 事件: 当客户端或服务器关闭连接时调用,或发生en内部错误。当客户机通过Close函数关闭连接时,它可以提供一个代码和一条消息,指示关闭的原因。服务器通常会响应我们的代码和消息。
● OnError 事件: 当无法连接到服务器时调用,发生内部错误或连接丢失时调用。第二个参数是一个异常对象,但它可以是null。在这种情况下,检查WebSocket的内部请求应该可以了解更多关于这个问题的信息。
● OnIncompleteFrame 事件: 参见高级Websocket主题中的流。
注册事件后,我们可以开始打开连接:
webSocket.Open();
在此步骤之后,我们将接收一个OnOpen事件,并可以开始向服务器发送消息。
// Sending out text messages:
webSocket.Send("Message to the Server");
// Sending out binary messages:
byte[] buffer = new byte[length];
//fill up the buffer with data
webSocket.Send(buffer);
所有完成后,我们应该关闭连接:
webSocket.Close();
Advanced WebSocket
● Ping :通过在接收OnOpen事件之前将StartPingThread属性设置为True,可以启动一个新线程向服务器发送Ping消息。通过这种方式,Ping消息将定期发送到服务器。两个ping之间的延迟可以在PingFrequency属性中设置(默认为1000ms)。
● Pong : 插件从服务器接收到的所有ping消息都会自动生成一个Pong答案。
● Streaming: 较长的文本或二进制消息将被分割。默认情况下,这些片段由插件自动组装。如果我们向WebSocket的OnIncompleteFrame事件注册一个事件处理程序,就可以覆盖这个机制。每次客户端接收到不完整的片段时都会调用此事件。这些片段将被插件忽略,它不会试图组装或存储它们。这个事件可以用来实现流体验。
连接到命名空间(Connecting to namespaces)
默认情况下,SocketManager在连接到服务器时将连接到根名称空间(“/”)。您可以通过SocketManager的套接字属性访问它:
Socket root = manager.Socket;
非默认名称空间可以通过GetSocket("/nspName")函数或通过管理器的indexer属性访问:
Socket nsp = manager["/customNamespace"];
// the same as this methode:
Socket nsp = manager.GetSocket("/customNamespace");
首先访问名称空间将启动内部连接进程。
订阅和接收事件(Subscribing and receiving events) 您可以订阅预定义的和自定义的事件。预定义的事件有“connect”、“connected”、“event”、“disconnect”、“reconnect”、“reconnection”、“reconnect_try”、“reconnect_failed”、“error”。自定义事件是程序员定义的事件,服务器将发送给客户机。您可以通过调用套接字的On函数订阅事件:
manager.Socket.On("login", OnLogin);
manager.Socket.On("new message", OnNewMessage);
事件处理程序将如下所示:
void OnLogin(Socket socket, Packet packet, params object[] args)
{
}
● socket套接字参数将是服务器发送此事件的名称空间-套接字对象。
●packet 包参数包含事件的内部包数据。包可用于访问服务器发送的二进制数据,或使用自定义Json解析器库解码负载数据。稍后将详细介绍这些。
●args参数是一个可变长度数组,包含数据包有效载荷数据中的解码对象。使用默认Json编码器,这些参数可以是“基本”类型(int、double、string)或对象列表(list <object>)或字典<string、对象>。
预先定义事件(Predefined events )
“connect”:名称空间打开时发送。
“connecting”:当SocketManager开始连接到套接字时发送。io服务器。
“event”:发送自定义(程序员定义)事件。
“disconnect连接”:当传输断开时发送,SocketManager关闭,Socket关闭,或者在握手数据指定的给定时间内没有从服务器接收到Pong消息时发送。
“reconnect”:当插件成功重新连接到套接字时发送。io服务器。
“reconnecting”:当插件试图重新连接到套接字时发送。io服务器。
●reconnect_try”:当插件试图重新连接到套接字时发送。io服务器。
“reconnect_failed”:当重新连接尝试无法连接到服务器,并且重新连接尝试达到选项“reconnectiontries”值时发送。
“error”:服务器或内部插件错误。事件的唯一参数将是一个BestHTTP.SocketIO。错误的对象。
其他与事件相关的功能:
Once:您可以订阅只调用一次的事件。
Off: 您可以删除所有事件订阅,或者只删除一个事件订阅。
发送事件(Sending events )
您可以使用Emit函数发送一个事件。您必须将事件名称作为第一个参数传递,还可以选择传递其他参数。这些将被编码成json并发送到服务器。您还可以选择设置一个回调函数,当服务器处理事件时将调用该函数(您必须正确设置服务器代码,以便能够发送回调函数)。套接字。IO服务器端文档以获取更多信息)。
// Send a custom event to the server with two parameters
manager.Socket.Emit("message", "userName", "message");
// Send an event and define a callback function that will be called as an
// acknowledgement of this event
manager.Socket.Emit("custom event", OnAckCallback, "param 1", "param 2");
void OnAckCallback(Socket socket, Packet originalPacket, params object[] args)
{
Debug.Log("OnAckCallback!");
}
向服务器发送确认您可以通过调用套接字的EmitAck函数将确认发送回服务器。您必须传递原始数据包和任何可选数据:
manager["/customNamespace"].On("customEvent", (socket, packet, args) => { socket.EmitAck(packet, "Event", "Received", "Successfully"); });
您可以保存对包的引用,并从其他地方调用EmitAck。
发送二进制数据(Sending binary data )
发送二进制(byte[])数据有两种方法: 通过传递到Emit函数,插件将扫描参数,如果找到一个参数,它将把它转换成二进制附件(如套接字中介绍的那样)。IO 1.0)。这是最有效的方法,因为它不会在客户端将字节数组转换为Base64编码的字符串,并在服务器端返回二进制。
byte[] data = new byte[10];
//...
manager.Socket.Emit("eventWithBinary", "textual param", data);
如果二进制数据作为字段或属性嵌入到对象中,Json编码器必须支持转换。默认Json编码器无法将嵌入的二进制数据转换为Json,您必须使用更高级的Json解析器库(如“Json .NET For Unity”—http://u3d.as/5q2)。
接收二进制数据(Receiving binary data )
在套接字,当二进制数据发送到客户机时,IO服务器将用Json对象替换数据({'_placeholder':true,'num':xyz}),并将二进制数据发送到另一个包中。在客户端,这些包将被收集并合并到一个包中。二进制数据将在包的Attachments属性中。在这里你也会有一些选项来使用这个包,在事件处理程序中,可以通过包的Attachments属性访问所有二进制数据。
Socket.On("frame", OnFrame);
void OnFrame(Socket socket, Packet packet, params object[] args)
{
texture.LoadImage(packet.Attachments[0]);
}
第二个选项与前一个选项几乎相同,只是略有改进:我们不将发送的Json字符串解码到c#对象。我们可以这样做,因为我们知道服务器只发送二进制数据,没有其他信息随此事件一起发送。所以我们会让插件知道不解码有效载荷:
// Subscribe to the "frame" event, and set the autoDecodePayload flag to false
Socket.On("frame", OnFrame, /*autoDecodePayload:*/ false);
void OnFrame(Socket socket, Packet packet, params object[] args)
{
// Use the Attachments property as before
texture.LoadImage(packet.Attachments[0]);
}
(默认情况下,autoDecodePayload参数为true。)
我们可以将“{'_placeholder':true,'num':xyz}”字符串替换回附件列表中的附件索引。
Socket.On("frame", OnFrame, /*autoDecodePayload:*/ false);
void OnFrame(Socket socket, Packet packet, params object[] args)
{
// Replace the Json object with the index
packet.ReconstructAttachmentAsIndex();
// now, decode the Payload to an object[]
args = packet.Decode(socket.Manager.Encoder);
// args now contains only an index number (probably 0)
byte[] data = packet.Attachments[Convert.ToInt32(args[0])];
texture.LoadImage(data);
}
我们可以将“{'_placeholder':true,'num':xyz}”字符串替换为附件中的二进制数据转换为Base64编码的字符串。高级Json解析器可以在必须将其设置为对象的字段或属性时将其转换为字节数组。
Socket.On("frame", OnFrame, /*autoDecodePayload:*/ false);
void OnFrame(Socket socket, Packet packet, params object[] args)
{
// Replace the Json object with the Base64 encoded string
packet.ReconstructAttachmentAsBase64();
// now, decode the Payload to an object[]
args = packet.Decode(socket.Manager.Encoder);
// args now contains a Base64 encoded string
byte[] data = Convert.FromBase64String(args[0] as string);
texture.LoadImage(data);
}
设置默认Json编码器(Set the default Json encoder)
通过将SocketManager的静态DefaultEncoder设置为一个新的编码器,可以更改默认的Json编码器。在此步骤之后,所有新创建的SocketManager都将使用此编码器。或者您可以直接将SocketManager对象的Encoder属性设置为Encoder。
编写自定义Json编码器(Writing a custom Json encoder)
如果出于各种原因想更改默认Json编码器,首先必须编写一个新的。为此,您必须编写一个新类来实现IJsonEncoder
引入BestHTTP.SocketIO.JsonEncoders namespace. 剥离的IJsonEncoder非常小,你只需要实现两个功能:
public interface IJsonEncoder {
List<object> Decode(string json);
string Encode(List<object> obj);
}
Decode函数必须将给定的json字符串解码为对象列表。因为插座的性质。IO协议,发送的json是一个数组,第一个元素是事件的名称。Encode函数用于对客户机想要发送到服务器的数据进行编码。这个列表的结构与Decode相同:列表的第一个元素是事件的名称,其他元素是用户发送的参数。
下面是一个完整的例子,使用示例文件夹中的LitJson库:
using LitJson;
public sealed class LitJsonEncoder : IJsonEncoder {
public List<object> Decode(string json)
{
JsonReader reader = new JsonReader(json);
return JsonMapper.ToObject<List<object>>(reader);
}
public string Encode(List<object> obj) {
JsonWriter writer = new JsonWriter();
JsonMapper.ToJson(obj, writer); return writer.ToString();
}
}
AutoDecodePayload
在“接收二进制数据”中已经讨论过AutoDecodePayload,但是您不仅可以为每个事件设置这个值,还可以为每个套接字设置这个值。套接字具有AutoDecodePayload属性,该属性用作事件订阅的默认值。它的默认值为true—所有有效负载已解码并发送到事件订阅服务器。如果你设置为false,插件不会解码,你必须自己解码。你不想每次都投args:当然!您可以在套接字对象上设置AutoDecodePayload,并且可以使用您喜爱的Json解析器将数据包的有效负载解码为强类型对象。但是请记住,有效负载将包含事件的名称,并且它是一个json数组。
身份验证(Authentication)
连接类具有AuthenticationProvider属性,可以将其设置为实现IAuthenticationProvider接口的对象。实现者必须实现以下属性和功能:
bool IsPreAuthRequired:属性,如果在连接类向服务器发出任何请求之前必须运行身份验证,则该属性返回true。示例:cookie验证器必须返回false,因为它必须发送用户凭证,并接收必须与请求一起发送的cookie。
StartAuthentication: 仅当IsPreAuthRequired为真时才需要的函数。否则它不会调用。
PrepareRequest: 使用请求和请求类型enum调用的函数。此函数可用于在将请求发送到服务器之前准备请求。
OnAuthenticationSucceded: 当IsPreAuthRequired为true时必须调用的事件验证过程成功
OnAuthenticationFailed:当IsPreAuthRequired为真且身份验证过程失败时必须调用的事件。
最后
以上就是坚定大地为你收集整理的Untiy中常用通信(HTTP)插件BestHttp转载至:https://blog.csdn.net/nliki/article/details/98958169的全部内容,希望文章能够帮你解决Untiy中常用通信(HTTP)插件BestHttp转载至:https://blog.csdn.net/nliki/article/details/98958169所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复