BestHttp文档翻译 2

2019-08-28  本文已影响0人  诸子百家谁的天下

WebSocket

介绍

var webSocket = new WebSocket(new Uri("wss://html5labs-interop.cloudapp.net/echo")); 
webSocket.OnOpen += OnWebSocketOpen; 
private void OnWebSocketOpen(WebSocket webSocket) { Debug.Log("WebSocket Open!"); }
webSocket.OnMessage += OnMessageReceived; 
private void OnMessageReceived(WebSocket webSocket, string message) { Debug.Log("Text Message received from server: " + message); } 
webSocket.OnBinary += OnBinaryMessageReceived; 
private void OnBinaryMessageReceived(WebSocket webSocket, byte[] message) { Debug.Log("Binary Message received from server. Length: " + message.Length); }
webSocket.OnClosed += OnWebSocketClosed; 
private void OnWebSocketClosed(WebSocket webSocket, UInt16 code, string message) { Debug.Log("WebSocket Closed!");}
webSocket.OnError += OnError; 
private void OnError(WebSocket ws, Exception ex) 
{ 
        string errorMsg = string .Empty; 
        if (ws.InternalRequest.Response != null)
        {
                 errorMsg = string.Format("Status Code from Server: {0} and Message: {1}", ws.InternalRequest.Response.StatusCode, ws.InternalRequest.Response.Message); 
        }
        Debug.Log("An error occured: " + (ex != null ? ex.Message : "Unknown: " + errorMsg)); 
} 
webSocket.OnErrorDesc += OnErrorDesc; 
void OnErrorDesc(WebSocket ws, string error) { Debug.Log("Error: " + error); } 
webSocket.Open(); 

在此步骤之后,我们将收到一个OnOpen事件的回调,我们可以开始向服务器发送消息。

// 发送字符串: 
webSocket.Send("Message to the Server"); 

// 创建二进制流,并填充: 
byte[] buffer = new byte[length]; 
//发送二进制流 
webSocket.Send(buffer); 

完成通信后/不需要的时候,关闭链接,无法重用已关闭的WebSocket实例。

webSocket.Close(); 

使用Socket.IO

    1):易于使用和熟悉的api
    2):兼容最新的Socket.IO规范
    3):从轮询传输到websocket传输的无缝升级
    4):断开时自动重新连接
    5):简单高效的二进制数据发送和多种接收方式
    6):在高级模式下使用它的强大工具(切换默认编码器,禁用自动解码等)
using System; using BestHTTP; 
using BestHTTP.SocketIO; 
var manager = new SocketManager(new Uri("http://chat.socket.io/socket.io/")); 
Socket root = manager.Socket; 

可以通过GetSocket('/ nspName')函数或通过manager的indexer属性访问非默认名称空间:

Socket nsp = manager["/customNamespace"]; 
// 等价于: 
Socket nsp = manager.GetSocket("/customNamespace"); 

首次访问命名空间将启动内部连接过程

manager.Socket.On("login", OnLogin); 
manager.Socket.On("new message", OnNewMessage); 

void OnLogin(Socket socket, Packet packet, params object[] args) 
{ 
        //Socket参数将是服务器发送此事件的namespace-socket对象
        //Packet参数包含事件的内部分组数据。数据包可用于访问服务器发送的二进制数据,或使用自定义Json解析器lib解码有效负载数据。稍后会详细介绍。
        //Args参数是一个可变长度数组,包含来自数据包有效负载数据的解码对象。使用默认的Json编码器,这些参数可以是“原始”类型(int,double,string)或对象列表(List对象)或Dictionary字符串,对象对象。
} 
//服务器上面的代码写法,在一个 node.js 的服务器上面
socket.emit('message', ‘MyNick’, ‘Msg to the client’); 
//客户端接收
// subscribe to the "message" event 
manager.Socket.On("message", OnMessage); 
// event handler 
void OnMessage(Socket socket, Packet packet, params object[] args) 
{ 
        // args[0] is the nick of the sender 
        // args[1] is the message 
        Debug.Log(string.Format("Message from {0}: {1}", args[0], args[1])); 
} 
    ●“connect”:命名空间打开时发送。 
    ●“connecting”:当SocketManager开始连接到socket.io服务器时发送。 
    ●“event”:在自定义(程序员定义的)事件上发送。 
    ●“disconnect”:当传输断开,SocketManager关闭,Socket关闭或在握手数据中指定的给定时间内没有从服务器收到Pong消息时发送。 
    ●“reconnect”:插件成功重新连接到socket.io服务器时发送。 
    ●“reconnecting”:当插件尝试重新连接到socket.io服务器时发送。 
    ●“reconnect_attempt”:当插件尝试重新连接到socket.io服务器时发送。 
    ●“reconnect_failed”:重新连接尝试无法连接到服务器并且ReconnectAttempt达到选项“ReconnectionAttempts”值时发送。 
    ●“error”:在服务器或内部插件错误上发送。事件的唯一参数是BestHTTP.SocketIO.Error对象。
    ● Once:您可以订阅仅被调用一次的事件。manager.Socket.Once("connect", OnConnected); 
    ● Off:您可以删除所有活动订阅,或只删除一个
    // 删除所有的回调事件
    manager.Socket.Off(); 
    //从"connect"事件中删除所有回调
    manager.Socket.Off("connect"); 
    //从"connect"事件中删除OnConnected回调
    manager.Socket.Off("connect", OnConnected); 
// 发送携带 2 个参数的事件给服务器
manager.Socket.Emit("message", "userName", "message"); 

// 发送携带 2 个参数的并有回调事件的事件给服务器
manager.Socket.Emit("custom event", OnAckCallback, "param 1", "param 2"); 
void OnAckCallback(Socket socket, Packet originalPacket, params object[] args) { Debug.Log("OnAckCallback!"); } 

您可以通过调用套接字的EmitAck函数向服务器发回确认。您必须传递原始数据包和任何可选数据,您可以保留对数据包的引用,并从其他位置调用EmitAck:

manager["/customNamespace"].On("customEvent", (socket, packet, args) => { socket.EmitAck(packet, "Event", "Received", "Successfully"); }); 
byte[] data = new byte[10]; 
manager.Socket.Emit("eventWithBinary", "textual param", data); 

2):如果二进制数据作为字段或属性嵌入对象中,则Json编码器必须支持转换。默认的Json编码器无法将嵌入的二进制数据转换为Json,您必须使用更高级的Json解析器库(如'JSON .NET For Unity' - http://u3d.as/5q2

Socket.On("frame", OnFrame); 
void OnFrame(Socket socket, Packet packet, params object[] args) { texture.LoadImage(packet.Attachments[0]); }

2):第二个选项与前一个选项几乎相同,略有改进:我们不会将发送的Json字符串解码为c#对象。我们可以这样做,因为我们知道服务器只发送了二进制数据,此事件没有其他信息。因此,我们将让插件知道不解码有效负载

//订阅“frame”事件,并将autoDecodePayload标志设置为false,不让插件自动解码
Socket.On("frame", OnFrame, /*autoDecodePayload:*/ false); 
void OnFrame(Socket socket, Packet packet, params object[] args) { texture.LoadImage(packet.Attachments[0]); } 

3):我们可以将'{'_placeholder':true,'num':xyz}'字符串替换为附件列表中附件的索引。

Socket.On("frame", OnFrame, /*autoDecodePayload:*/ false); 
void OnFrame(Socket socket, Packet packet, params object[] args) 
{ 
        //用索引替换Json对象
        packet.ReconstructAttachmentAsIndex(); 
        // 现在,将Payload解码为 object[]
        args = packet.Decode(socket.Manager.Encoder); 
        // args现在只包含一个索引号(可能为0) 
        byte[] data = packet.Attachments[Convert.ToInt32(args[0])]; texture.LoadImage(data); 
} 

4):我们可以用附件中转换为Base64编码字符串的二进制数据替换'{'_ placeholder':true,'num':xyz}'字符串。当高级Json解析器必须将其设置为对象的字段或属性时,它可以将其转换为字节数组

Socket.On("frame", OnFrame, /*autoDecodePayload:*/ false); 
void OnFrame(Socket socket, Packet packet, params object[] args) 
{ 
        // 用Base64编码的字符串替换Json对象 packet.ReconstructAttachmentAsBase64(); 
        // 现在,将Payload解码为object[]
        args = packet.Decode(socket.Manager.Encoder); 
        // args现在包含一个Base64编码的字符串
        byte[] data = Convert.FromBase64String(args[0] as string); texture.LoadImage(data); 
}
public interface IJsonEncoder 
{ 
        List<object> Decode(string json); 
        string Encode(List<object> obj); 
} 

Decode函数必须将给定的json字符串解码为对象列表。由于Socket.IO协议的性质,发送的json是一个数组,第一个元素是事件的名称。Encode函数用于编码客户端要发送给服务器的数据。此列表的结构与Decode相同:列表的第一个元素是事件的名称,任何其他元素是用户发送的参数。例子:

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(); 
        } 
} 
Socket.On(SocketIOEventTypes.Error, OnError); 
void OnError(Socket socket, Packet packet, params object[] args) 
{ 
        Error error = args[0] as Error; 
        switch (error.Code) 
        { 
                case SocketIOErrors.User: 
                        Debug.Log("Exception in an event handler!"); 
                break; 
                case SocketIOErrors.Internal: 
                        Debug.Log("Internal error!"); 
                break; 
                default: 
                        Debug.Log("Server error!"); break; 
        } 
        Debug.Log(error.ToString()); 
} 
    1):Reconnection:断开连接后是否自动重新连接。其默认值为true
    2):ReconnectionAttempts:放弃前的尝试次数。它的默认值是Int.MaxValu
    3):ReconnectionDelay:在尝试重新连接之前最初等待的时间。受+/- RandomizationFactor影响。例如,默认初始延迟将在500ms到1500ms之间。其默认值为10000毫秒。
    4):ReconnectionDelayMax:重新连接之间等待的最长时间。如上所述,每次尝试都会增加重新连接延迟以及随机化。其默认值为5000毫秒。
    5):RandomizationFactor:它可用于控制ReconnectionDelay范围。其默认值为0.5,可以在0..1值之间设置
    6)Timeout:发出“connect_error”和“connect_timeout”事件之前的连接超时。它不是底层tcp套接字的连接超时,而是socket.io协议。其默认值为20000ms
    7):AutoConnect:通过将此设置为false,您必须在决定适当时调用SocketManager的Open()。
    8):ConnectWith:So​​cketManager将尝试连接到此属性的传输集。它可以是TransportTypes.Polling或TransportTypes.WebSocket

SignalR

    1):兼容最新的SignalR服务器实现
    2):好用的 API
    3):传输回调
    4):重新连接逻辑
    5):支持所有Hub功能
using BestHTTP.SignalR;
Uri uri = new Uri("http://besthttpsignalr.azurewebsites.net/raw-connection/");
//通过仅将服务器的uri传递给构造函数来创建没有集线器的连接。
Connection signalRConnection = new Connection(uri); 
//通过将集线器名称传递给构造函数来创建与集线器的连接。
Connection signalRConnection = new Connection(uri, "hub1", "hub2", "hubN"); 
//通过将Hub对象传递给构造函数来创建与Hub的连接。
Hub hub1 = new Hub("hub1"); 
Hub hub2 = new Hub("hub2"); 
Hub hubN = new Hub("hubN"); 
Connection signalRConnection = new Connection(uri, hub1, hub2, hubN); 
//创建Connection之后,我们可以通过调用Open()函数开始连接到服务器
signalRConnection.Open(); 
//OnConnected:当连接类成功连接并且SignalR协议用于通信时,将触发此事件。
signalRConnection.OnConnected += (con) => Debug.Log("Connected to the SignalR server!"); 

//OnClosed:当SignalR协议关闭时,将触发此事件,并且不再发送或接收更多消息。
signalRConnection.OnClosed += (con) => Debug.Log("Connection Closed"); 
//OnError:发生错误时调用。如果连接已打开,插件将尝试重新连接,否则连接将关闭。
signalRConnection.OnError += (conn, err) => Debug.Log("Error: " + err); 

//OnReconnecting:启动重新连接尝试时会触发此事件。在此事件之后,将调用OnError或OnReconnected事件。可以在OnReconnected / OnClosed事件之前触发多个OnReconnecting-OnError事件对,因为插件将尝试在给定时间内多次重新连接。
signalRConnection.OnReconnecting += (con) => Debug.Log("Reconnecting"); 

//OnReconnected:重新连接尝试成功时触发。
signalRConnection.OnReconnecting += (con) => Debug.Log("Reconnected"); 
//OnStateChnaged:连接状态发生变化时触发。事件处理程序将同时接收旧状态和新状态。
signalRConnection.OnStateChanged += (conn, oldState, newState) => Debug.Log(string.Format("State Changed {0} -> {1}", oldState, newState)); 

//OnNonHubMessage:当服务器向客户端发送非集线器消息时触发。客户端应该知道服务器期望的消息类型,并且应该相应地转换接收的对象。
signalRConnection.OnNonHubMessage + =(con,data)= Debug.Log('来自服务器的消息:'+ data.ToString());

//RequestPreparator:为每个发出并将发送到服务器的HTTPRequest调用此委托。它可用于进一步自定义请求。
signalRConnection.RequestPreparator = (con, req, type) => req.Timeout = TimeSpan.FromSeconds(30); 

//将非集线器消息发送到服务器很容易,因为调用连接对象上的函数:
signalRConnection.Send(new { Type = "Broadcast", Value = "Hello SignalR World!" }); 

//此函数将使用Connection的JsonEncoder将给定对象编码为Json字符串,并将其发送到服务器。已编码的Json字符串可以使用SendJson函数发送
signalRConnection.SendJson("{ Type: ‘Broadcast’, Value: ‘Hello SignalR World!’ }"); 
//可以通过索引或名称通过Connection对象访问Hub实例。
Hub hub = signalRConnection[0]; 
Hub hub = signalRConnection["hubName"]; 

// 注册服务器可调用方法,要处理服务器可调用方法调用,我们必须调用集线器的On函数:
signalRConnection["hubName"].On("joined", Joined); 
void Joined(Hub hub, MethodCallMessage msg) { Debug.log(string.Format("{0} joined at {1}", msg.Arguments[0], msg.Arguments[1])); }

MethodCallMessage是服务器发送的对象,包含以下属性:

    Hub:包含方法必须调用的集线器名称的字符串。
    Method:包含方法名称的字符串
    Arguments:包含方法调用参数的对象数组。它可以是一个空数组。
    State:包含其他自定义数据的字典

该插件将使用Hub和Method属性将消息路由到正确的集线器和事件处理程序。处理方法调用的函数只能使用Arguments和State属性。

//在没有任何参数的情况下调用服务器端函数
signalRConnection["hubName"].Call("Ping"); 
//使用两个字符串参数调用服务器端函数:'param1'和'param2'
signalRConnection["hubName"].Call("Message", "param1", "param2"); 

Call(string method ,OnMethodResultDelegate onResult,params object [] args):此函数可以用作前一个函数,但是函数可以作为第二个参数传递,该参数将在成功调用服务器端函数时调用。

signalRConnection["hubName"].Call("GetValue", OnGetValueDone); 
void OnGetValueDone(Hub hub, ClientMessage originalMessage, ResultMessage result) { Debug.Log("GetValue executed on the server. Return value of the function:" + result.ReturnValue.ToString()); } 

此回调函数接收调用此函数的Hub,发送到服务器的原始ClientMessage消息以及由于方法调用而由服务器发送的ResultMessage实例。 ResultMessage对象包含ReturnValue和State属性。
如果方法的返回类型为void,则ReturnValue为null.
Call(string method,OnMethodResultDelegate onResult,OnMethodFailedDelegate onError,params object [] args):此函数可用于指定当方法无法在服务器上运行时将调用的回调。由于方法调用中存在未找到的方法,错误的参数或未处理的异常,因此可能会发生故障

signalRConnection["hubName"].Call("GetValue", OnGetValueDone, OnGetValueFailed); 
void OnGetValueFailed(Hub hub, ClientMessage originalMessage, FailureMessage error) 
{ 
        Debug.Log("GetValue failed. Error message from the server: " + error.ErrorMessage); 
} 

FailureMessage包含以下属性:

    ○ IsHubError:如果是Hub错误,则为True。 
    ○ ErrorMessage:有关错误本身的简短消息。 
    ○ StackTrace:如果在服务器上打开了详细的错误报告,则它包含错误的堆栈跟踪。
    ○ AdditionalData:如果它不为null,则它包含有关错误的其他信息。

Call(string method,OnMethodResultDelegate onResult,OnMethodFailedDelegate onError,OnMethodProgressDelegate onProgress,params object [] args):此函数可用于向服务器端方法调用添加其他进度消息处理程序。对于长时间运行的作业,服务器可以将进度消息发送到客户端。

signalRConnection["hubName"].Call("GetValue", OnGetValueDone, OnGetValueFailed, OnGetValueProgress); 
void OnGetValueProgress(Hub hub, ClientMessage originalMessage, ProgressMessage progress) 
{ 
       Debug.Log(string.Format("GetValue progressed: {0}%", progress.Progress)); 
}

当插件收到ResultMessage或FailureMessage时,它不会为这些消息之后的ProgressMessages提供服务。

class SampleHub : Hub 
{ 
       // 默认构造函数。每个集线器都必须有一个有效的名称. 
       public SampleHub() :base("SampleHub") 
       { 
               // 注册服务器可调用函数 
               base.On("ClientFunction", ClientFunctionImplementation); 
       }
       // 私有函数实现服务器可调用函数
       private void ClientFunctionImplementation(Hub hub, MethodCallMessage msg) 
       { 
       // TODO: implement 
       } 
       // 包装函数调用服务器端函数.
       public void ServerFunction(string argument) 
       { 
               base.Call("ServerFunction", argument); 
       } 
}
//可以实例化此SampleHub并将其传递给Connection的构造函数:
SampleHub sampleHub = new SampleHub(); Connection signalRConnection = new Connection(Uri, sampleHub); 

    ● bool IsPreAuthRequired:如果在Connection类向服务器发出任何请求之前必须运行身份验证,则返回true的属性。示例:cookie身份验证器必须返回false,因为它必须发送用户凭据并接收必须随请求一起发送的cookie。 
    ● StartAuthentication:仅在IsPreAuthRequired为true时才需要的函数。否则它不会被调用。 
    ● PrepareRequest:使用请求和请求类型枚举调用的函数。此函数可用于在将请求发送到服务器之前准备。 
    ● OnAuthenticationSucceded:IsPreAuthRequired为true且身份验证过程成功时必须调用的事件。 
    ● OnAuthenticationFailed:IsPreAuthRequired为true且身份验证过程失败时必须调用的事件。

一个非常简单的基于Header的身份验证器看起来像这样:

class HeaderAuthenticator : IAuthenticationProvider 
{ 
       public string User { get; private set; } 
       public string Roles { get; private set; } 
       // 此类身份验证不需要预先验证步骤
       public bool IsPreAuthRequired { get { return false; } } 
       //未使用的事件,因为IsPreAuthRequired为false 
       public event OnAuthenticationSuccededDelegate OnAuthenticationSucceded; 
       //未使用的事件,因为IsPreAuthRequired为false
       public event OnAuthenticationFailedDelegate OnAuthenticationFailed; 
       // 使用用户名和角色初始化身份验证器的构造函数.
       public HeaderAuthenticator(string  user, string roles) 
       { 
               this.User = user; this.Roles = roles; 
       } 
       //未使用的事件,因为IsPreAuthRequired为false             
       public void StartAuthentication() { } 
       // 通过向其添加两个标头来准备请求
       public void PrepareRequest(BestHTTP.HTTPRequest request, RequestTypes type) 
       { 
               request.SetHeader("username", this.User); request.SetHeader("roles", this.Roles); 
       }
}

与Socket.IO的Manager类一样,SignalR的Connection类具有JsonEncoder属性,也可以设置静态Connection.DefaultEncoder。 JsonEncoder必须从BestHTTP.SignalR.JsonEncoders命名空间实现IJsonEncoder接口。该软件包包含一个LitJsonEncoder示例,也可用于某些示例

Server-Sent Events

//The EventSource class 
//EventSource类位于BestHTTP.ServerSentEvents命名空间中:
using BestHTTP.ServerSentEvents; 
var sse = new EventSource(new Uri("http://server.com")); 
    ● Uri:这是协议尝试连接的端点。它是通过构造函数设置的。 
    ● State:EventSource对象的当前状态。 
    ● ReconnectionTime:等待尝试重新连接尝试的时间。它的默认值是2秒。 
    ● LastEventId:最后收到的事件的id。如果没有收到任何事件ID,它将为null。 
    ● InternalRequest:将在Open函数中发送的内部HTTPRequest对象。
//OnOpen:成功升级协议时调用它
eventSource.OnOpen += OnEventSourceOpened; 
void OnEventSourceOpened(EventSource source) { Debug.log("EventSource Opened!"); } 

//OnMessage:当客户端从服务器收到新消息时调用它。此函数将接收一个Message对象,该对象包含Data属性中消息的有效内容。每次客户端收到消息时都会调用此事件,即使消息具有有效的事件名称,我们也为此事件分配了一个事件处理程序!
eventSource.OnMessage += OnEventSourceMessage;
void OnEventSourceMessage(EventSource source, Message msg) { Debug.log("Message: " + msg.Data); }

// OnError:在连接到服务器或处理数据流时遇到错误时调用
eventSource.OnError += OnEventSourceError; 
void OnEventSourceError(EventSource source, string error) { Debug.log("Error: " + error); }

//OnRetry:在插件尝试重新连接到服务器之前调用此函数。如果函数返回false,则不会进行任何尝试,并且将关闭EventSource。
eventSource.OnRetry += OnEventSourceRetry; 
bool OnEventSourceRetry(EventSource source) { // disable retry return false; }

//OnClosed:当EventSource关闭时,将调用此事件。
eventSource.OnClosed += OnEventSourceClosed; 
void OnEventSourceClosed(EventSource source) { Debug.log("EventSource Closed!"); } 

//OnStateChanged:每次State属性更改时调用。
eventSource.OnStateChanged += OnEventSourceStateChanged; 
void OnEventSourceStateChanged(EventSource source, States oldState, States newState) { Debug.log(string.Format("State Changed {0} => {1}", oldSate, newState))); }
//Open: 调用此函数,插件将开始连接到服务器并升级到Server-Sent Events协议。
EventSource eventSource = new EventSource(new Uri("http://server.com")); 
eventSource.Open(); 

// On:使用此功能,客户端可以订阅事件
eventSource.On("userLogon", OnUserLoggedIn); 
void OnUserLoggedIn(EventSource source, Message msg) { Debug.log(msg.Data); }

//Off:它可用于取消订阅活动。
eventSource.Off("userLogon"); 

//Close: 此函数将开始关闭EventSource对象。
eventSource.Close(); 
 ● Id:已发送事件的ID。如果没有发送id,则可以为null。它被插件使用。 
 ● 事件:事件的名称。如果没有发送事件名称,则可以为null。 
 ● 数据:消息的实际有效负载。 
 ● 重试:服务器发送插件在重新连接尝试之前应等待的时间。它被插件使用。

简单例子

● Upload a picture using forms

var request = new HTTPRequest(new Uri("http://server.com"), HTTPMethods.Post, onFinished); 
request.AddBinaryData("image", texture.EncodeToPNG(), "image.png"); 
request.Send(); 

● Upload a picture without forms, sending only the raw data

var request = new HTTPRequest(new Uri("http://server.com"), HTTPMethods.Post, onFinished); 
request.SetHeader("Content-Type", "image/png"); 
request.Raw = texture.EncodeToPNG(); 
request.Send(); 

● Add custom header

var request = new HTTPRequest(new Uri("http://server.com"), HTTPMethods.Post, onFinished); 
request.SetHeader("Content-Type", "application/json; charset=UTF-8"); 
request.RawData = UTF8Encoding.GetBytes(ToJson(data)); 
request.Send(); 

● Display download progress

var request = new HTTPRequest(new Uri("http://serveroflargefile.net/path"), (req, resp) => { Debug.Log("Finished!"); }); 
request.OnProgress += (req, down, length) => Debug.Log(string.Format("Progress: {0:P2}", down / (float)length)); 
request.Send(); 

● Abort a request

var request = new HTTPRequest(new Uri(address), (req, resp) => { // State should be HTTPRequestStates.Aborted if we call Abort() before // it’s finishes Debug.Log(req.State); }); 
request.Send(); 
request.Abort();

● 可恢复下载的范围请求,第一个请求是获取服务器功能的Head请求。当支持范围请求时,将调用DownloadCallback函数。在这个函数中,我们将创建一个新的实际请求来获取内容的块,并将回调函数设置为此函数。当前下载位置保存到PlayerPrefs,因此即使在应用程序重新启动后也可以恢复下载。

private const int ChunkSize = 1024 * 1024; // 1 MiB - should be bigger! 
private string saveTo = "downloaded.bin"; 
void StartDownload(string url) 
{
      var headRequest = new HTTPRequest(new Uri(url), HTTPMethods.Head, (request, response) => 
      {
        if (response == null) Debug.LogError("Response null. Server unreachable? Try again later."); 
        else {
                if (response.StatusCode == 416) Debug.LogError("Requested range not satisfiable"); else if (response.StatusCode == 200) 
                Debug.LogError("Partial content doesn't supported by the server, content can be downloaded as a whole."); 
                else if (response.HasHeaderWithValue("accept-ranges","none")) Debug.LogError("Server doesn't supports the 'Range' header! The file can't be downloaded in parts."); 
                else DownloadCallback(request, response);         
        }
      }  
      // Range header for our head request 
      int startPos = PlayerPrefs.GetInt("LastDownloadPosition",0); 
      headRequest.SetRangeHeader(startPos, startPos + ChunkSize); 
      headRequest.DisableCache = true; headRequest.Send(); 
} 

void DownloadCallback(HTTPRequest request, HTTPResponse response) 
{
        if (response == null) { Debug.LogError("Response null. Server unreachable, or connection lost? Try again later."); return; } var range = response.GetRange(); 
        if (range == null) { Debug.LogError("No 'Content-Range' header returned from the server!"); return; } 
        else if (!range.IsValid) { Debug.LogError("No valid 'Content-Range' header returned from the server!"); return; } 
        if (request.MethodType != HTTPMethods.Head) 
        { 
                string path = Path.Combine(Application.temporaryCachePath,saveTo); 
                using (FileStream fs = new FileStream(path, FileMode.Append)) fs.Write(response.Data, 0, response.Data.Length); 
                PlayerPrefs.SetInt("LastDownloadPosition", range.LastBytePos); 
                Debug.LogWarning(string.Format("Download Status: {0}-{1}/{2}", range.FirstBytePos, range.LastBytePos, range.ContentLength)); 
                if (range.LastBytePos == range.ContentLength - 1) { Debug.LogWarning("Download finished!"); return; } 
        }
        var downloadRequest = new HTTPRequest(request.Uri, HTTPMethods.Get, /*isKeepAlive:*/ true, DownloadCallback); 
        int nextPos = 0; 
        if (request.MethodType != HTTPMethods.Head) nextPos = range.LastBytePos + 1; else nextPos = PlayerPrefs.GetInt("LastDownloadPosition", 0);
        downloadRequest.SetRangeHeader(nextPos, nextPos + ChunkSize); 
        downloadRequest.DisableCache = true;    
        downloadRequest.Send(); 
} 

其他

    ●BESTHTTP_DISABLE_COOKIES:使用此定义可以禁用所有与cookie相关的代码。不会进行cookie解析,保存和发送。 
    ●BESTHTTP_DISABLE_CACHING:使用此定义可以禁用所有与缓存相关的代码。不会进行缓存或缓存验证。 
    ●BESTHTTP_DISABLE_SERVERSENT_EVENTS:可以使用此功能禁用服务器发送的事件。 SignalR不会回退到此。 
    ●BESTHTTP_DISABLE_WEBSOCKET:可以使用此禁用Websocket。 SignalR和Socket.IO不会使用此协议。 
    ●BESTHTTP_DISABLE_SIGNALR:将禁用整个SignalR实施。 
    ●BESTHTTP_DISABLE_SIGNALR_CORE:将禁用SignalR Core实施。 
    ●BESTHTTP_DISABLE_SOCKETIO:将禁用整个Socket.IO实现。 
    ●BESTHTTP_DISABLE_ALTERNATE_SSL:如果您没有为WebSocket使用HTTPS或WSS,或者您对默认实现感到满意,则可以禁用备用ssl处理程序。 
    ●BESTHTTP_DISABLE_UNITY_FORM:您可以删除对Unity的WWWForm的依赖。
    ● WebGL
    ● iOS
    ● Android
    ● Windows Phone 10
    ● WinRT / Metro / Windows应用商店应用8.1,10•Windows,Linux和Mac独立版
上一篇下一篇

猜你喜欢

热点阅读