using BestHTTP.ServerSentEvents;
using BestHTTP.SignalR;
using BestHTTP.SignalR.Hubs;
using BestHTTP.SignalR.Messages;
using BestHTTP.SocketIO;
using LitJson;
using System;
using System.Collections.Generic;
using UnityEngine;
public class MySocketIO : MonoBehaviour
{
void Start()
{
}
void ConnectingToNamespaces()
{
SocketManager manager = new SocketManager(new Uri("http://chat.socket.io/socket.io/"));
//默认情况下,SocketManager将在连接到服务器时连接到根(“/”)名称空间。 你可以通过SocketManager的Socket属性来访问它:
Socket root = manager.Socket;
//可以通过GetSocket(“/ nspName”)函数或通过管理器的索引器属性访问非默认名称空间:
Socket nsp1 = manager["/customNamespace"];
Socket nsp2 = manager.GetSocket("/customNamespace");
//首次访问命名空间将会启动内部连接程序
}
//您可以订阅预定义和自定义事件
void SubscribingAndReceivingEvents()
{
SocketManager manager = new SocketManager(new Uri("http://chat.socket.io/socket.io/"));
//预定义事件
//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对象
//自定义事件
//自定义事件是程序员定义的事件,您的服务器将发送给您的客户端。 您可以通过调用套接字的On函数来订阅事件
manager.Socket.On("login", (socket, packet, args) => { });
//socket参数将是服务器发送此事件的名称空间套接字对象。
//packet参数包含事件的内部数据包数据。 该数据包可用于访问服务器发送的二进制数据,或使用定制的Json解析器库来解码有效载荷数据
//args参数是一个可变长度数组,它包含来自数据包有效负载数据的已解码对象。
//使用默认的Json编码器,这些参数可以是对象的“原始”类型(int,double,string)或对象列表(List )或Dictionary
//服务器上发出的消息
//socket.emit('message', ‘MyNick’, ‘Msg to the client’);
//客户端捕获的消息
//订阅message事件
manager.Socket.On("message", (socket, packet, args) =>
{
Debug.Log(string.Format("Message from {0}: {1}", args[0], args[1]));
});
//其它与事件相关的功能
//Once:你可以订阅一个仅调用一次的事件
manager.Socket.Once("connect", (socket, packet, args) => { });
//off:取消订阅
manager.Socket.Off();//取消所有订阅的事件
manager.Socket.Off("connect");//对事件"connect"取消订阅
//manager.Socket.Off("connect", OnConnected); //从事件"connect"中移除OnConnected方法
}
//向服务器发送确认
void SendingAcknowledgement()
{
SocketManager manager = new SocketManager(new Uri(""));
//可以通过调用套接字的EmitAck函数向服务器发回确认
//必须传递原始数据包和可选数据:
manager["/customNamespace"].On("customEvent", (socket, packet, args) =>
{
socket.EmitAck(packet, "Event", "Received", "Successfully");
});
}
void SendingBinaryData()
{
SocketManager manager = new SocketManager(new Uri(""));
//有两种方式发送二进制数据
//一.
//通过Emit函数传递,插件将扫描参数,如果它找到一个参数,它会将其转换为二进制附件(如在Socket.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)
}
void ReceivingBinaryData()
{
SocketManager manager = new SocketManager(new Uri(""));
//在Socket.IO服务器中,当二进制数据发送到客户端时,它将用Json对象({'_ placeholder':true,'num':xyz})替换数据,并将二进制数据发送到另一个数据包中
//在客户端,这些数据包将被收集并合并为一个数据包
//二进制数据将位于数据包的Attachments属性中
//在这里你将有一些使用这个数据包的选择:
//一、
//在事件处理函数中,您可以通过数据包的Attachments属性访问所有二进制数据
manager.Socket.On("frame", (socket, packet, args) =>
{
Texture2D texture2D = new Texture2D(0, 0);
texture2D.LoadImage(packet.Attachments[0]);
});
//二、
//第二个选项与前一个选项几乎相同,但有一点改进:我们不会将发送的Json字符串解码为c#对象
//我们可以这样做,因为我们知道服务器只发送二进制数据,这个事件没有其他信息。 所以我们会让插件知道不要解码有效载荷:
manager.Socket.On("frame", (socket, packet, args) =>
{
Texture2D texture2D = new Texture2D(0, 0);
texture2D.LoadImage(packet.Attachments[0]);
}, false);
//三、
//我们可以将“{'_placeholder':true,'num':xyz}”字符串替换为Attachments列表中Attachment的索引
manager.Socket.On("frame", (socket, packet, args) =>
{
//用索引替换Json对象
packet.ReconstructAttachmentAsIndex();
//解码有效载荷到object[]
args = packet.Decode(socket.Manager.Encoder);
//现在args只包含一个索引号(可能是0)
byte[] data = packet.Attachments[Convert.ToInt32(args[0])];
Texture2D texture2D = new Texture2D(0, 0);
texture2D.LoadImage(data);
}, false);
}
//设置默认的Json编码器
//如果由于各种原因想要更改默认的Json编码器,首先必须编写一个新的Json编码器
//为此,必须编写一个新的类,该类可以从BestHTTP.SocketIO.JsonEncoders命名空间实现IJsonEncoder
//剥离的IJsonEncoder非常小,你只需要实现两个功能:
public interface IJsonEncoder
{
List Decode(string json);
string Encode(List obj);
}
//Decode函数必须将给定的json字符串解码为对象列表。 由于Socket.IO协议的性质,发送的json是一个数组,第一个元素是事件的名称。
//编码功能用于对客户想要发送到服务器的数据进行编码
//该列表的结构与Decode相同:列表的第一个元素是事件的名称,其他任何元素都是用户发送的参数。
//下面是使用示例文件夹中的LitJson库的完整示例:
public sealed class LitJsonEncoder : IJsonEncoder
{
public List Decode(string json)
{
JsonReader reader = new JsonReader(json);
return JsonMapper.ToObject>(reader);
}
public string Encode(List obj)
{
JsonWriter writer = new JsonWriter();
JsonMapper.ToJson(obj, writer); return writer.ToString();
}
}
//发生服务器端或客户端错误时发生的“error”事件
//事件的第一个参数将是一个Error对象。 这将包含Code属性中的错误代码和Message属性中的字符串消息
//ToString()函数在这个类中被覆盖,你可以使用这个函数写出它的内容。
void ErrorHandling()
{
SocketManager manager = new SocketManager(new Uri(""));
manager.Socket.On(SocketIOEventTypes.Error, (socket, packet, 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());
});
}
//可以将SocketOptions实例传递给SocketManager的构造函数
//可以更改以下选项:
//Reconnection:断开连接后是否自动重新连接。它的默认值是true。
//ReconnectionAttempts:放弃之前的尝试次数。它的默认值是Int.MaxValue
//ReconnectionDelay:尝试重新连接之前最初等待多久。受+/- RandomizationFactor影响。例如,默认的初始延迟将在500毫秒到1500毫秒之间。其默认值是10000ms
//ReconnectionDelayMax:重新连接之间等待的最长时间。如上所述,每次尝试都会增加重新连接延迟以及随机化。其默认值是5000ms
//RandomizationFactor:它可以用来控制ReconnectionDelay范围。它的默认值是0.5,可以在0..1的值之间进行设置。
//Timeout:发出“connect_error”和“connect_timeout”事件之前的连接超时。它不是底层的tcp套接字的连接超时,而是socket.io协议。它的默认值是20000ms
//AutoConnect:通过将此设置为false,只要您决定适当,就必须调用SocketManager的Open()。
//当你创建一个新的SocketOptions对象时,它的属性被设置为它们的默认值。
//BestHTTP.SignalR命名空间中的Connection类管理到SignalR服务器的抽象连接
//连接到SignalR服务器从创建Connection对象开始
//该类将跟踪协议的当前状态并将触发事件
void TheConnectionClass()
{
//你有多个途径创建一个连接对象
Uri uri = new Uri("");
//通过仅将服务器的URI传递给构造函数来创建连接,而不使用集线器
Connection signlRConnection1 = new Connection(uri);
//通过将集线器名称也传递给构造函数来创建连接,并使用集线器
Connection signlRConnection2 = new Connection(uri, "hub1", "hub2", "hubN");
//通过将Hub对象传递给构造函数来创建连接,并使用Hub
//using BestHTTP.SignalR.Hubs;
Hub hub1 = new Hub("hub1");
Hub hub2 = new Hub("hub2");
Hub hubN = new Hub("hubN");
Connection signlRConnection3 = new Connection(uri, hub1, hub2, hubN);
//在创建Connection之后,我们可以通过调用Open()函数来开始连接到服务器
signlRConnection1.Open();
}
void HandlingGeneralEvents()
{
Uri uri = new Uri("");
Connection signlRConnection = new Connection(uri);
//Connection类将允许您订阅多个事件,这些事件如下:
//OnConnected:当connection类成功连接并且SignalR协议用于通信时,会触发此事件
signlRConnection.OnConnected += (con) => Debug.Log("Connected to the SignalR server!");
//OnClosed:当SignalR协议关闭时会触发此事件,并且不再发送或接收更多消息
signlRConnection.OnClosed += (con) => Debug.Log("Connection Closed");
//OnError:发生错误时调用。 如果连接已经打开,插件将尝试重新连接,否则连接将被关闭
signlRConnection.OnError += (con, err) => Debug.Log("Error: " + err);
//OnReconnecting:当重新连接尝试开始时,将触发此事件
//在此事件之后,将调用OnError或OnReconnected事件
//在OnReconnected / OnClosed事件之前可以触发多个OnReconnecting-OnError事件对,因为插件会在给定的时间内尝试重新连接多次。
signlRConnection.OnReconnecting += (con) => Debug.Log("Reconnecting");
//OnReconnected:重新连接尝试成功时触发
signlRConnection.OnReconnecting += (con) => Debug.Log("Reconnected");
//OnStateChnaged:连接状态改变时触发,事件处理程序将收到旧状态和新状态
signlRConnection.OnStateChanged += (conn, oldState, newState) => Debug.Log(string.Format("State Changed {0} -> {1}", oldState, newState));
//OnNonHubMessage:服务器向客户端发送非集线器消息时触发
//客户端应该知道服务器期望的消息类型,并且应该相应地转换接收到的对象
signlRConnection.OnNonHubMessage += (con, data) => Debug.Log("Message from server: " + data.ToString());
//RequestPreparator:每个HTTPRequest都会调用这个委托并将发送到服务器, 它可以用来进一步自定义请求
signlRConnection.RequestPreparator = (con, req, type) => req.Timeout = TimeSpan.FromSeconds(30);
}
void SendingNonHubMessages()
{
Uri uri = new Uri("");
Connection signlRConnection = new Connection(uri);
//发送非集线器消息到服务器像在connection对象上调用一个函数一样容易:
signlRConnection.Send(new { Type = "Broadcast", Value = "Hello SignalR World!" });
//此函数将使用Connection的JsonEncoder将给定对象编码为Json字符串,并将其发送到服务器
//已编码的Json字符串可以使用SendJson函数发送:
signlRConnection.SendJson("{ Type: ‘Broadcast’, Value: ‘Hello SignalR World!’ }");
}
//为了定义客户端上Hub可以从服务器调用的方法,并在服务器上调用Hub上的方法,必须将Hubs添加到Connection对象中
//这可以通过将集线器名称或集线器实例添加到Connection构造函数中完成,在Connection Class部分中已进行演示
//using BestHTTP.SignalR.Messages;
void Hubs()
{
Uri uri = new Uri("");
Connection signalRConnection = new Connection(uri);
//Accessing hubs (访问集线器)
Hub hub1 = signalRConnection[0];
Hub hub2 = signalRConnection["hubName"];
//Register server callable methods(注册服务器可调用方法)
//要处理服务器可调用方法的调用,我们必须调用集线器的On函数:
signalRConnection["hubName"].On("joined", (hub, msg) =>
{
Debug.Log(string.Format("{0} joined at {1}", msg.Arguments[0], msg.Arguments[1]));
});
//On的第二个参数类型是MethodCallMessage
//MethodCallMessage是一个服务器发送的对象,它包含以下属性:
//Hub:包含该方法必须调用的集线器名称的字符串
//Method:包含方法名称的字符串
//Arguments:包含方法调用参数的对象数组。 它可以是一个空数组
//State:包含其他自定义数据的字典。
//该插件将使用Hub和Method属性将消息发送到正确的集线器和事件处理程序。 处理方法调用的函数只能使用参数和状态属性
//Call server-side methods (调用服务端方法)
//调用服务器端方法可以通过调用Hub的Call函数来完成
//重载的调用函数能够满足每个需求
//Call函数是非阻塞函数,它们不会阻塞,直到服务器发送有关该呼叫的任何消息
//重载如下:
//Call(string method, params object[] args):
//这可用于以fireand -forget样式调用服务器端函数
//我们不会收到有关方法调用成功或失败的消息。 这个函数可以在没有任何'args'参数的情况下调用,以调用无参数方法
signalRConnection["hubName"].Call("Ping");
signalRConnection["hubName"].Call("Message", "param1", "param2");
//Call(string method, OnMethodResultDelegate onResult, params object[] args):
//该函数可以作为前一个函数使用,但函数可以传递第二个参数,该函数将在服务器端函数成功调用时调用。
signalRConnection["hubName"].Call("GetValue", OnGetValueDone);
//此回调函数接收调用此函数的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);
//FailureMessage包含以下属性:
//IsHubError:如果是Hub错误,则为True
//ErrorMessage:关于错误本身的简短消息
//StackTrace:如果服务器上打开了详细的错误报告,那么它将包含错误的堆栈跟踪
//AdditionalData:如果不为空,则它包含有关错误的其他信息
// Call(string method, OnMethodResultDelegate onResult, OnMethodFailedDelegate onError, OnMethodProgressDelegate onProgress, params object[] args):
//该函数可用于向服务器端方法调用添加额外的进度消息处理程序。 对于长时间运行的作业,服务器可以向客户端发送进度消息
signalRConnection["hubName"].Call("GetValue", OnGetValueDone, OnGetValueFailed, OnGetValueProgress);
}
void OnGetValueDone(Hub hub, ClientMessage originalMessage, ResultMessage result)
{
Debug.Log("GetValue executed on the server. Return value of the function:" + result.ReturnValue.ToString());
}
void OnGetValueFailed(Hub hub, ClientMessage originalMessage, FailureMessage error)
{
Debug.Log("GetValue failed. Error message from the server: " + error.ErrorMessage);
}
void OnGetValueProgress(Hub hub, ClientMessage originalMessage, ProgressMessage progress)
{
Debug.Log(string.Format("GetValue progressed: {0}%", progress.Progress));
}
void EventSourceClass()
{
EventSource eventSource = new EventSource(new Uri("http://server.com"));
//Properties
//这些是EventSource类公开公开的属性:
//Uri:这是协议尝试连接的端点。 它通过构造函数设置。
//State:EventSource对象的当前状态。
//ReconnectionTime:尝试执行重新连接尝试需要等待多少时间。 它的默认值是2秒。
//LastEventId:最近收到的事件的ID。 如果没有接收到事件ID,它将为空。
//InternalRequest:将在Open函数中发出的内部HTTPRequest对象。
//Events
//OnOpen:它在协议成功升级时调用
eventSource.OnOpen += (source) => Debug.Log("EventSource Opened!");
//OnMessage:它在客户端收到来自服务器的新消息时调用
//该函数将接收一个Message对象,该对象包含Data属性中消息的有效内容
//每次客户端收到消息时都会调用此事件,即使消息具有有效的事件名称,并且我们为此事件分配了事件处理程序!
eventSource.OnError += (source, error) => Debug.Log("Error: " + error);
//OnRetry:在插件尝试重新连接到服务器之前调用此函数。 如果函数返回false,则不会进行尝试,并关闭EventSource。
eventSource.OnRetry += (source) => false;
//OnClosed:EventSource关闭时会调用此事件
eventSource.OnClosed += (source) => Debug.Log("EventSource Closed!");
//OnStateChanged:当State属性变化时调用
eventSource.OnStateChanged += (source, oldState, newState) => Debug.Log(string.Format("State Changed {0} => {1}", oldState, newState));
//Functions
//这些是EventSource对象的公有方法
// Open: 调用此函数后,插件将开始连接到服务器并升级到ServerSent Events协议
eventSource.Open();
// On: 使用这个功能,客户端可以订阅事件
eventSource.On("userLogon", (source, msg) => { Debug.Log(msg.Data); });
// Off: 它可以用来取消订阅活动
eventSource.Close();
}
// Message类是一个逻辑单元,它包含服务器可以发送的所有信息。
//属性
//Id:发送事件的ID。 如果没有ID发送,可以为空。 它由插件使用。
//Event:事件的名称。 如果没有发送事件名称,则可以为null。
//Data:消息的实际有效负载。
//Retry:服务器发送插件在尝试重新连接之前等待的时间。 它由插件使用
void SampleHubImplement()
{
Uri uri = new Uri("");
SampleHub sampleHub = new SampleHub();
Connection signalRConnection = new Connection(uri, sampleHub);
}
}
class SampleHub : Hub
{
// Default constructor. Every hubs have to have a valid name.
public SampleHub() : base("SampleHub")
{
// 注册一个服务器可调用函数
base.On("ClientFunction", ClientFunctionImplementation);
}
// 实现服务器可调用函数的私有函数
private void ClientFunctionImplementation(Hub hub, MethodCallMessage msg)
{
// 待办事项:实现
}
// 包装器函数调用服务器端函数。
public void ServerFunction(string argument)
{
base.Call("ServerFunction", argument);
}
}