BestHttp介绍2

  1. WebSocket
    1. 介绍
    2. 使用Socket.IO

WebSocket

介绍

  • 1:我们可以通过WebSocket类使用WebSocket功能。我们只需要将服务器的Uri传递给WebSocket的构造函数

  • 2:创建

        var webSocket = new WebSocket(new Uri("wss://html5labs-interop.cloudapp.net/echo")); 
  • 3:,OnOpen事件:在建立与服务器的连接时调用。在此事件回调之后,WebSocket的IsOpen属性将为True,直到我们或服务器关闭连接或发生错误。
        webSocket.OnOpen += OnWebSocketOpen; 
        private void OnWebSocketOpen(WebSocket webSocket) { Debug.Log("WebSocket Open!"); }
  • 4:,OnMessage事件:从服务器收到文本消息时调用。
        webSocket.OnMessage += OnMessageReceived; 
        private void OnMessageReceived(WebSocket webSocket, string message) { Debug.Log("Text Message received from server: " + message); } 
  • 5:,OnBinary事件:从服务器收到二进制blob消息时调用。
        webSocket.OnBinary += OnBinaryMessageReceived; 
        private void OnBinaryMessageReceived(WebSocket webSocket, byte[] message) { Debug.Log("Binary Message received from server. Length: " + message.Length); }
  • 6:,OnClosed事件:在客户端或服务器关闭连接时调用,或发生内部错误。当客户端通过Close函数关闭连接时,它可以提供代码和消息,指示关闭的原因。服务器通常会回复我们的代码和消息。
        webSocket.OnClosed += OnWebSocketClosed; 
        private void OnWebSocketClosed(WebSocket webSocket, UInt16 code, string message) { Debug.Log("WebSocket Closed!");}
  • 7:OnError事件:当我们无法连接到服务器时调用,发生内部错误或连接丢失。第二个参数是Exception对象,但它可以为null。在这种情况下,检查WebSocket的InternalRequest应该告诉更多有关该问题的信息。
        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)); 
        } 
  • 8:OnErrorDesc事件:一个更具信息性的事件,此事件在OnError事件之后调用,因为后者仅使用Exception参数调用。但它可以提供更详细的错误报告。
        webSocket.OnErrorDesc += OnErrorDesc; 
        void OnErrorDesc(WebSocket ws, string error) { Debug.Log("Error: " + error); } 
  • 9:在我们将所有事件注册完备之后,我们可以开始连接:
        webSocket.Open(); 

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

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

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

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

        webSocket.Close(); 
  • 10:Ping消息:通过在收到OnOpen事件之前将StartPingThread属性设置为True,可以启动新线程将Ping消息发送到服务器。这样,Ping消息将定期发送到服务器。可以在PingFrequency属性中设置两次ping之间的延迟(默认值为1000ms).(相当于设置心跳包)
  • 11:Pong消息:从插件服务器收到的所有ping消息将自动生成Pong应答。
  • 12:Streaming:较长的文本或二进制消息将变得支离破碎。默认情况下,这些片段由插件自动组装。如果我们向WebSocket的OnIncompleteFrame事件注册事件处理程序,则可以覆盖此机制。每次客户端收到不完整的片段时都会调用此事件。这些片段将被插件忽略,它不会尝试组装这些片段,也不会存储它们。此事件可用于实现流式传输体验。(自定义组装消息).

使用Socket.IO

  • 1:Socket.IO实现使用插件已有的功能。当轮询传输与其所有功能(cookie,连接重用等)一起使用时,它将发送HTTPRequests以获取握手数据,发送和接收数据包。 WebSocket实现用于WebSocket传输
      1):易于使用和熟悉的api
      2):兼容最新的Socket.IO规范
      3):从轮询传输到websocket传输的无缝升级
      4):断开时自动重新连接
      5):简单高效的二进制数据发送和多种接收方式
      6):在高级模式下使用它的强大工具(切换默认编码器,禁用自动解码等)
  • 2:使用.如果要连接到Socket.IO服务,可以使用BestHTTP.SocketIO.SocketManager类来完成。首先,您必须创建一个SocketManager实例
        using System; using BestHTTP; 
        using BestHTTP.SocketIO; 
        var manager = new SocketManager(new Uri("http://chat.socket.io/socket.io/")); 
  • 3:Url中的/socket.io/路径非常重要,默认情况下,Socket.IO服务器将侦听此查询。所以不要忘记测试!
  • 4:Connecting to namespaces ,默认情况下,SocketManager将在连接到服务器时连接到根(“/”)命名空间。您可以通过SocketManager的Socket属性访问它:
        Socket root = manager.Socket; 

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

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

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

  • 4:Subscribing and receiving events ,您可以订阅预定义和自定义事件。预定义事件是“连接”,“连接”,“事件”,“断开连接”,“重新连接”,“重新连接”,“重新连接”,“重新连接失败”,“错误”。(“connect”, “connecting”, “event”, “disconnect”, “reconnect”, “reconnecting”, “reconnect_attempt”, “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参数将是服务器发送此事件的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); 
>>>

* 5:Sending events ,您可以使用“Emit”功能发送事件。您必须将事件名称作为第一个参数和可选的其他参数传递。这些将被编码为json并将被发送到服务器。您可以选择设置一个回调函数,该函数将在服务器处理事件时被调用(您必须正确设置服务器代码才能发回回调函数。有关更多信息,请参阅Socket.IO服务器端文档)。
    // 发送携带 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"); }); 
* 6:发送二进制数据有 2 种方法
1):通过传递给Emit函数,插件将扫描参数,如果找到参数,它将把它转换为二进制附件(如Socket.IO 1.0中所介绍的)。这是最有效的方法,因为它不会将字节数组转换为客户端的Base64编码字符串,并在服务器端转换为二进制。
    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)
* 7:接收二进制数据
在Socket.IO服务器中,当二进制数据发送到客户端时,它将用Json对象({'_ placeholder':true,'num':xyz})替换数据,并将二进制数据发送到另一个数据包中。在客户端,这些数据包将被收集并合并到一个数据包中。二进制数据将位于数据包的Attachments属性中。
1):在这里你也可以选择使用这个数据包:
在事件处理程序中,您可以通过数据包的Attachments属性访问所有二进制数据,autoDecodePayload默认为 true
    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); 
    }
* 8:设置默认的Json编码器, 您可以通过将SocketManager的静态DefaultEncoder设置为新的编码器来更改默认的Json编码器。在此步骤之后,所有新创建的SocketManager将使用此编码器。或者,您可以直接将SocketManager对象的Encoder属性设置为编码器。
编写自定义Json编码器:如果由于各种原因想要更改默认的Json编码器,首先必须编写一个新的Json编码器。为此,您必须编写一个新类,该类从BestHTTP.SocketIO.JsonEncoders命名空间实现IJsonEncoder。剥离的IJsonEncoder非常小,你必须只实现两个功能:
    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(); 
            } 
    } 

* 9:AutoDecodePayload属性,
已经在“接收二进制数据”中讨论过AutoDecodePayload,但是您不仅可以按event设置此值,还可以设置每个socket的值。socket具有AutoDecodePayload属性,该属性用作事件订阅的默认值。其默认值为true - 所有Payload都已解码并分派给事件订阅者。如果设置为false,插件将不进行解码,您必须自己完成。
你不想每次都抛出args:当然!您可以在Socket对象上设置AutoDecodePayload,并且可以使用您喜欢的Json解析器将Packet的Payload解码为强类型对象。但请记住,Payload将包含事件的名称,它是一个json数组。示例Payload如下所示:'['eventName',{'field':'stringValue'},{'field':1.0}]'。

* 10:Error handling  发生服务器端或客户端错误时发出“错误”事件。事件的第一个参数是Error对象。这将包含Code属性中的错误代码和Message属性中的字符串消息。此类中的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()); 
    } 

* 11:SocketOptions类中的可用选项,您可以将SocketOptions实例传递给SocketManager的构造函数。您可以更改以下选项:
>>>
        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:像Socket.IO这样的SignalR实现使用了插件的基本功能。 HTTPRequests和WebSockets用于连接和通信连接池。 Cookie随请求一起发送,记录器用于记录有关协议和错误的信息,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(); 
* 2:Handling general events Connection类允许您订阅多个事件。这些事件如下:
    //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); 

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

    //此函数将使用Connection的JsonEncoder将给定对象编码为Json字符串,并将其发送到服务器。已编码的Json字符串可以使用SendJson函数发送
    signalRConnection.SendJson("{ Type: ‘Broadcast’, Value: ‘Hello SignalR World!’ }"); 

* 4:Hubs,为了在客户端上定义Hub可以从服务器调用的方法,并调用a上的方法
服务器上的集线器必须将集线器添加到Connection对象。这可以通过将集线器名称或集线器实例添加到Connection构造函数来完成,在“连接类”部分中进行了演示
    //可以通过索引或名称通过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属性。

* 5:Call server-side methods 
调用服务器端方法可以通过调用Hub的Call函数来完成。调用函数重载以满足每个需求。 Call函数是非阻塞函数,它们不会阻塞,直到服务器发回有关该调用的任何消息。
* 6:重载函数:
Call(string method,params object [] args):这可以用来以一种即发即弃的方式调用服务器端函数。我们不会收到有关方法调用成功或失败的任何消息。可以在没有任何'args'参数的情况下调用此函数来调用无参数方法
    //在没有任何参数的情况下调用服务器端函数
    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提供服务。

 * 7:使用Hub类作为继承的基类,Hub类可以用作封装集线器功能的基类。
    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); 

 * 8:Authentication
 Connection类具有AuthenticationProvider属性,可以将其设置为实现IAuthenticationProvider接口的对象,实现者必须实现以下属性和功能
 >>>
        ● 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

* 1:Server-Sent Events是一种基于字符串的单向协议。数据来自服务器,没有选项可以向服务器发送任何内容。它是使用最新的草案实现的。虽然协议的名称是Server-Sent Events,但类本身名为EventSource,发生错误时,一旦发送LastEventId,插件将尝试重新连接,让服务器发送任何我们应该收到的缓冲消息
    //The EventSource class 
    //EventSource类位于BestHTTP.ServerSentEvents命名空间中:
    using BestHTTP.ServerSentEvents; 
    var sse = new EventSource(new Uri("http://server.com")); 
* 2:Properties,这些是EventSource类的公开公开属性:
>>>
        ● Uri:这是协议尝试连接的端点。它是通过构造函数设置的。 
        ● State:EventSource对象的当前状态。 
        ● ReconnectionTime:等待尝试重新连接尝试的时间。它的默认值是2秒。 
        ● LastEventId:最后收到的事件的id。如果没有收到任何事件ID,它将为null。 
        ● InternalRequest:将在Open函数中发送的内部HTTPRequest对象。
>>>

* 3:事件
    //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))); }

* 4:Functions,这些是EventSource对象的公共函数。
    //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(); 
* 5:Message,Message类是一个逻辑单元,包含服务器可以发送的所有信息,Properties:
>>>
     ● 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(); 
    } 

## 其他

* 1:禁用功能
>>>
        ●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的依赖。
>>>
* 2:支持的平台
>>>
        ● WebGL
        ● iOS
        ● Android
        ● Windows Phone 10
        ● WinRT / Metro / Windows应用商店应用8.1,10•Windows,Linux和Mac独立版
>>>
* 3:在Android,iOS和桌面平台上.net的Net SslStream用于HTTPS。这可以处理各种证书,但有些证书可能会失败。要提供备用解决方案BouncyCastle捆绑在插件中,您可以通过在HTTPRequest对象上将UseAlternateSSL设置为true来使用它。但它也可能在一些认证上失败。在Windows Phone 8.1(及更高版本)和WinRT(Windows应用商店应用程序)上,安全的Tls 1.2协议将处理连接。

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1487842110@qq.com

Love

Title:BestHttp介绍2

文章字数:7.9k

Author:诸子百家-谁的天下?

Created At:2020-05-11, 11:41:32

Updated At:2021-03-28, 02:59:27

Url:http://yoursite.com/2020/05/11/Unity/BestHttp/WebSocket/

Copyright: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

爱你,爱世人