开始开发
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.XR.XREAL.Samples.NetWork
|
||||
{
|
||||
public class LocalServerSearcher : SingletonMonoBehaviour<LocalServerSearcher>
|
||||
{
|
||||
public struct ServerInfoResult
|
||||
{
|
||||
public bool isSuccess;
|
||||
public IPEndPoint endPoint;
|
||||
}
|
||||
public delegate void OnGetSearchResult(ServerInfoResult result);
|
||||
private UdpClient client;
|
||||
private IPEndPoint endpoint;
|
||||
private Thread m_ReceiveThread = null;
|
||||
private const string SEARCHSERVERIP = "FIND-SERVER";
|
||||
private const int BroadCastPort = 6001;
|
||||
private static float TimeoutWaittingTime = 3f;
|
||||
private IPEndPoint m_LocalServer;
|
||||
private Queue<OnGetSearchResult> m_Tasks = new Queue<OnGetSearchResult>();
|
||||
private Coroutine m_TimeOutCoroutine = null;
|
||||
|
||||
public LocalServerSearcher()
|
||||
{
|
||||
client = new UdpClient(new IPEndPoint(IPAddress.Any, 0));
|
||||
endpoint = new IPEndPoint(IPAddress.Parse("255.255.255.255"), BroadCastPort);
|
||||
}
|
||||
|
||||
public void Search(OnGetSearchResult callback)
|
||||
{
|
||||
lock (m_Tasks)
|
||||
{
|
||||
m_Tasks.Enqueue(callback);
|
||||
}
|
||||
|
||||
if (m_ReceiveThread == null)
|
||||
{
|
||||
m_ReceiveThread = new Thread(new ThreadStart(RecvThread));
|
||||
m_ReceiveThread.IsBackground = true;
|
||||
m_ReceiveThread.Start();
|
||||
}
|
||||
|
||||
RequestForServerIP();
|
||||
TryStopTimeOutCoroutine();
|
||||
m_TimeOutCoroutine = StartCoroutine(TimeOut());
|
||||
}
|
||||
|
||||
private void TryStopTimeOutCoroutine()
|
||||
{
|
||||
if (m_TimeOutCoroutine != null)
|
||||
{
|
||||
Debug.Log("[LocalServerSearcher] StopTimeOutCoroutine");
|
||||
StopCoroutine(m_TimeOutCoroutine);
|
||||
m_TimeOutCoroutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void RequestForServerIP()
|
||||
{
|
||||
Debug.Log("[LocalServerSearcher] RequestForServerIP");
|
||||
byte[] buf = Encoding.Default.GetBytes(SEARCHSERVERIP);
|
||||
client.Send(buf, buf.Length, endpoint);
|
||||
}
|
||||
|
||||
private void RecvThread()
|
||||
{
|
||||
IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, BroadCastPort);
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] buf = client.Receive(ref endpoint);
|
||||
string data = Encoding.Default.GetString(buf);
|
||||
Debug.Log("[LocalServerSearcher] Get the server info:" + data);
|
||||
if (!string.IsNullOrEmpty(data))
|
||||
{
|
||||
string[] param = data.Split(':');
|
||||
if (param != null && param.Length == 2)
|
||||
{
|
||||
m_LocalServer = new IPEndPoint(IPAddress.Parse(param[0]), int.Parse(param[1]));
|
||||
Response(m_LocalServer);
|
||||
TryStopTimeOutCoroutine();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator TimeOut()
|
||||
{
|
||||
float time_last = 0f;
|
||||
while (true)
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
time_last += Time.deltaTime;
|
||||
if (time_last > TimeoutWaittingTime)
|
||||
{
|
||||
Debug.Log("[LocalServerSearcher] Get the server TimeOut");
|
||||
Response(null);
|
||||
TryStopTimeOutCoroutine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Response(IPEndPoint endpoint)
|
||||
{
|
||||
if (m_Tasks.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
XREALMainThreadDispatcher.Singleton.QueueOnMainThread(() =>
|
||||
{
|
||||
ServerInfoResult result = new ServerInfoResult();
|
||||
result.endPoint = endpoint;
|
||||
result.isSuccess = endpoint != null;
|
||||
|
||||
lock (m_Tasks)
|
||||
{
|
||||
if (m_Tasks.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var callback = m_Tasks.Dequeue();
|
||||
while (callback != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
callback?.Invoke(result);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
if (m_Tasks.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
callback = m_Tasks.Dequeue();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1361081bc4466d94796ef0d593a960d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.XR.XREAL.Samples.NetWork
|
||||
{
|
||||
/// <summary> Net msg type. </summary>
|
||||
public enum MessageType
|
||||
{
|
||||
/// <summary> Empty type. </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary> Connect server. </summary>
|
||||
Connected = 1,
|
||||
/// <summary> Disconnect from server. </summary>
|
||||
Disconnect = 2,
|
||||
|
||||
/// <summary> Heart beat. </summary>
|
||||
HeartBeat = 3,
|
||||
/// <summary> Enter room. </summary>
|
||||
EnterRoom = 4,
|
||||
/// <summary> Enter room. </summary>
|
||||
ExitRoom = 5,
|
||||
|
||||
/// <summary> An enum constant representing the update camera Parameter option. </summary>
|
||||
UpdateCameraParam = 6,
|
||||
|
||||
/// <summary> Used to synchronization message with the server. </summary>
|
||||
MessageSynchronization = 7,
|
||||
}
|
||||
|
||||
/// <summary> (Serializable) an enter room data. </summary>
|
||||
[Serializable]
|
||||
public class EnterRoomData
|
||||
{
|
||||
/// <summary> Enter room result. </summary>
|
||||
public bool result;
|
||||
}
|
||||
|
||||
/// <summary> (Serializable) an exit room data. </summary>
|
||||
[Serializable]
|
||||
public class ExitRoomData
|
||||
{
|
||||
/// <summary> Exit room result. </summary>
|
||||
public bool Suc;
|
||||
}
|
||||
|
||||
/// <summary> (Serializable) a camera parameter. </summary>
|
||||
[Serializable]
|
||||
public class CameraParam
|
||||
{
|
||||
/// <summary> Camera fov. </summary>
|
||||
public Fov4f fov;
|
||||
}
|
||||
|
||||
|
||||
/// <summary> (Serializable) a fov 4f. </summary>
|
||||
[Serializable]
|
||||
public class Fov4f
|
||||
{
|
||||
/// <summary> The left. </summary>
|
||||
public double left;
|
||||
/// <summary> The right. </summary>
|
||||
public double right;
|
||||
/// <summary> The top. </summary>
|
||||
public double top;
|
||||
/// <summary> The bottom. </summary>
|
||||
public double bottom;
|
||||
|
||||
/// <summary> Default constructor. </summary>
|
||||
public Fov4f() { }
|
||||
|
||||
/// <summary> Constructor. </summary>
|
||||
/// <param name="l"> A double to process.</param>
|
||||
/// <param name="r"> A double to process.</param>
|
||||
/// <param name="t"> A double to process.</param>
|
||||
/// <param name="b"> A double to process.</param>
|
||||
public Fov4f(double l, double r, double t, double b)
|
||||
{
|
||||
this.left = l;
|
||||
this.right = r;
|
||||
this.top = t;
|
||||
this.bottom = b;
|
||||
}
|
||||
|
||||
/// <summary> Convert this object into a string representation. </summary>
|
||||
/// <returns> A string that represents this object. </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} {1} {2} {3}", left, right, top, bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a310a14a9e3851f47a5041b928fdf819
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,211 @@
|
||||
using LitJson;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.XR.XREAL.Samples.NetWork
|
||||
{
|
||||
/// <summary> An observer view net worker. </summary>
|
||||
public class NetWorkBehaviour
|
||||
{
|
||||
/// <summary> The net work client. </summary>
|
||||
protected NetWorkClient m_NetWorkClient;
|
||||
|
||||
/// <summary> The limit waitting time. </summary>
|
||||
private const float limitWaittingTime = 5f;
|
||||
/// <summary> True if is connected, false if not. </summary>
|
||||
private bool m_IsConnected = false;
|
||||
/// <summary> True if is jonin success, false if not. </summary>
|
||||
private bool m_IsJoninSuccess = false;
|
||||
/// <summary> True if is closed, false if not. </summary>
|
||||
private bool m_IsClosed = false;
|
||||
private Coroutine checkServerAvailableCoroutine = null;
|
||||
private Dictionary<ulong, Action<JsonData>> _ResponseEvents = new Dictionary<ulong, Action<JsonData>>();
|
||||
|
||||
public virtual void Listen()
|
||||
{
|
||||
if (m_NetWorkClient == null)
|
||||
{
|
||||
m_NetWorkClient = new NetWorkClient();
|
||||
m_NetWorkClient.OnDisconnect += OnDisconnect;
|
||||
m_NetWorkClient.OnConnect += OnConnected;
|
||||
m_NetWorkClient.OnJoinRoomResult += OnJoinRoomResult;
|
||||
m_NetWorkClient.OnMessageResponse += OnMessageResponse;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMessageResponse(byte[] data)
|
||||
{
|
||||
ulong msgid = BitConverter.ToUInt64(data, 0);
|
||||
|
||||
Action<JsonData> callback;
|
||||
if (!_ResponseEvents.TryGetValue(msgid, out callback))
|
||||
{
|
||||
Debug.LogWarning("[NetWorkBehaviour] can not find the msgid bind event:" + msgid);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the header to get the msg.
|
||||
byte[] result = new byte[data.Length - sizeof(ulong)];
|
||||
Array.Copy(data, sizeof(ulong), result, 0, result.Length);
|
||||
string json = Encoding.UTF8.GetString(result);
|
||||
callback?.Invoke(JsonMapper.ToObject(json));
|
||||
Debug.Log("[NetWorkBehaviour] OnMessageResponse hit...");
|
||||
_ResponseEvents.Remove(msgid);
|
||||
}
|
||||
|
||||
/// <summary> Check server available. </summary>
|
||||
/// <param name="ip"> The IP.</param>
|
||||
/// <param name="callback"> The callback.</param>
|
||||
public void CheckServerAvailable(string ip, int port, Action<bool> callback)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ip))
|
||||
{
|
||||
callback?.Invoke(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (checkServerAvailableCoroutine != null)
|
||||
{
|
||||
XREALMainThreadDispatcher.Singleton.StopCoroutine(checkServerAvailableCoroutine);
|
||||
}
|
||||
checkServerAvailableCoroutine = XREALMainThreadDispatcher.Singleton.StartCoroutine(CheckServerAvailableCoroutine(ip, port, callback));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Check server available coroutine. </summary>
|
||||
/// <param name="ip"> The IP.</param>
|
||||
/// <param name="callback"> The callback.</param>
|
||||
/// <returns> An IEnumerator. </returns>
|
||||
private IEnumerator CheckServerAvailableCoroutine(string ip, int port, Action<bool> callback)
|
||||
{
|
||||
Debug.Log($"[ObserverView] CheckServerAvailableCoroutine: {ip}:{port}");
|
||||
// Start to connect the server.
|
||||
m_NetWorkClient.Connect(ip, port);
|
||||
float timeLast = 0;
|
||||
while (!m_IsConnected)
|
||||
{
|
||||
if (timeLast > limitWaittingTime || m_IsClosed)
|
||||
{
|
||||
Debug.Log("[ObserverView] Connect the server TimeOut!");
|
||||
callback?.Invoke(false);
|
||||
yield break;
|
||||
}
|
||||
timeLast += Time.deltaTime;
|
||||
yield return new WaitForEndOfFrame();
|
||||
}
|
||||
// Start to enter the room.
|
||||
m_NetWorkClient.EnterRoomRequest();
|
||||
|
||||
timeLast = 0;
|
||||
while (!m_IsJoninSuccess)
|
||||
{
|
||||
if (timeLast > limitWaittingTime || m_IsClosed)
|
||||
{
|
||||
Debug.Log("[ObserverView] Join the server TimeOut!");
|
||||
callback?.Invoke(false);
|
||||
yield break;
|
||||
}
|
||||
timeLast += Time.deltaTime;
|
||||
yield return new WaitForEndOfFrame();
|
||||
}
|
||||
callback?.Invoke(true);
|
||||
}
|
||||
|
||||
public void SendMsg(JsonData data, Action<JsonData> onResponse, float timeout = 3)
|
||||
{
|
||||
XREALMainThreadDispatcher.Singleton.StartCoroutine(SendMessage(data, onResponse, timeout));
|
||||
}
|
||||
|
||||
private IEnumerator SendMessage(JsonData data, Action<JsonData> onResponse, float timeout)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
Debug.LogError("[NetWorkBehaviour] data is null!");
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Add msgid(current timestamp) as the header.
|
||||
ulong msgid = (ulong)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
byte[] json_data = Encoding.UTF8.GetBytes(data.ToJson());
|
||||
byte[] total_data = new byte[json_data.Length + sizeof(ulong)];
|
||||
Array.Copy(BitConverter.GetBytes(msgid), 0, total_data, 0, sizeof(ulong));
|
||||
Array.Copy(json_data, 0, total_data, sizeof(ulong), json_data.Length);
|
||||
|
||||
if (onResponse != null)
|
||||
{
|
||||
Action<JsonData> onResult;
|
||||
AsyncTask<JsonData> asyncTask = new AsyncTask<JsonData>(out onResult);
|
||||
_ResponseEvents[msgid] = onResult;
|
||||
m_NetWorkClient.SendMessage(total_data);
|
||||
|
||||
XREALMainThreadDispatcher.Singleton.StartCoroutine(SendMsgTimeOut(msgid, timeout));
|
||||
yield return asyncTask.WaitForCompletion();
|
||||
|
||||
onResponse?.Invoke(asyncTask.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NetWorkClient.SendMessage(total_data);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator SendMsgTimeOut(UInt64 id, float timeout)
|
||||
{
|
||||
yield return new WaitForSeconds(timeout);
|
||||
|
||||
Action<JsonData> callback;
|
||||
if (_ResponseEvents.TryGetValue(id, out callback))
|
||||
{
|
||||
Debug.LogWarningFormat("[NetWorkBehaviour] Send msg timeout, id:{0}", id);
|
||||
JsonData json = new JsonData();
|
||||
json["success"] = false;
|
||||
callback?.Invoke(json);
|
||||
}
|
||||
}
|
||||
|
||||
#region Net msg
|
||||
/// <summary> Executes the 'connected' action. </summary>
|
||||
private void OnConnected()
|
||||
{
|
||||
Debug.Log("[NetWorkBehaviour] OnConnected...");
|
||||
m_IsConnected = true;
|
||||
}
|
||||
|
||||
/// <summary> Executes the 'disconnect' action. </summary>
|
||||
private void OnDisconnect()
|
||||
{
|
||||
Debug.Log("[NetWorkBehaviour] OnDisconnect...");
|
||||
this.Close();
|
||||
}
|
||||
|
||||
/// <summary> Executes the 'join room result' action. </summary>
|
||||
/// <param name="result"> True to result.</param>
|
||||
private void OnJoinRoomResult(bool result)
|
||||
{
|
||||
Debug.Log("[NetWorkBehaviour] OnJoinRoomResult :" + result);
|
||||
m_IsJoninSuccess = result;
|
||||
if (!result)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary> Closes this object. </summary>
|
||||
public virtual void Close()
|
||||
{
|
||||
if (checkServerAvailableCoroutine != null)
|
||||
{
|
||||
XREALMainThreadDispatcher.Singleton.StopCoroutine(checkServerAvailableCoroutine);
|
||||
}
|
||||
m_NetWorkClient.ExitRoomRequest();
|
||||
m_NetWorkClient?.Dispose();
|
||||
m_NetWorkClient = null;
|
||||
checkServerAvailableCoroutine = null;
|
||||
m_IsClosed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20c6ebff010f798409adcf0cfa4b57fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.XR.XREAL.Samples.NetWork
|
||||
{
|
||||
/// <summary> A net work client. </summary>
|
||||
public class NetWorkClient : IDisposable
|
||||
{
|
||||
/// <summary> Event queue for all listeners interested in OnJoinRoomResult events. </summary>
|
||||
public event Action<bool> OnJoinRoomResult;
|
||||
/// <summary> Event queue for all listeners interested in OnCameraParamUpdate events. </summary>
|
||||
public event Action<CameraParam> OnCameraParamUpdate;
|
||||
/// <summary> Event queue for all listeners interested in OnMessageResponse events. </summary>
|
||||
public event Action<byte[]> OnMessageResponse;
|
||||
/// <summary> Event queue for all listeners interested in OnDisconnect events. </summary>
|
||||
public event Action OnDisconnect;
|
||||
/// <summary> Event queue for all listeners interested in OnConnect events. </summary>
|
||||
public event Action OnConnect;
|
||||
|
||||
|
||||
/// <summary> Default constructor. </summary>
|
||||
public NetWorkClient()
|
||||
{
|
||||
NetworkSession.Register(MessageType.Connected, OnConnected);
|
||||
NetworkSession.Register(MessageType.Disconnect, OnDisConnected);
|
||||
NetworkSession.Register(MessageType.HeartBeat, HeartbeatResponse);
|
||||
NetworkSession.Register(MessageType.EnterRoom, EnterRoomResponse);
|
||||
NetworkSession.Register(MessageType.ExitRoom, ExitRoomResponse);
|
||||
NetworkSession.Register(MessageType.UpdateCameraParam, UpdateCameraParamResponse);
|
||||
NetworkSession.Register(MessageType.MessageSynchronization, MessageSynchronizationResponse);
|
||||
}
|
||||
|
||||
/// <summary> Join the server's room. </summary>
|
||||
public void EnterRoomRequest()
|
||||
{
|
||||
NetworkSession.Enqueue(MessageType.EnterRoom);
|
||||
}
|
||||
|
||||
/// <summary> Exit room request. </summary>
|
||||
public void ExitRoomRequest()
|
||||
{
|
||||
NetworkSession.Enqueue(MessageType.ExitRoom);
|
||||
}
|
||||
|
||||
/// <summary> Updates the camera parameter request. </summary>
|
||||
public void UpdateCameraParamRequest()
|
||||
{
|
||||
NetworkSession.Enqueue(MessageType.UpdateCameraParam);
|
||||
}
|
||||
|
||||
/// <summary> Join the server's room. </summary>
|
||||
public void SendMessage(byte[] data)
|
||||
{
|
||||
NetworkSession.Enqueue(MessageType.MessageSynchronization, data);
|
||||
}
|
||||
|
||||
/// <summary> Connects. </summary>
|
||||
/// <param name="ip"> The IP.</param>
|
||||
/// <param name="port"> The port.</param>
|
||||
public void Connect(string ip, int port)
|
||||
{
|
||||
NetworkSession.Connect(ip, port);
|
||||
}
|
||||
|
||||
/// <summary> Executes the 'connected' action. </summary>
|
||||
/// <param name="data"> The data.</param>
|
||||
private void OnConnected(byte[] data)
|
||||
{
|
||||
OnConnect?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged
|
||||
/// resources. </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
NetworkSession.UnRegister(MessageType.Connected, OnConnected);
|
||||
NetworkSession.UnRegister(MessageType.Disconnect, OnDisConnected);
|
||||
NetworkSession.UnRegister(MessageType.HeartBeat, HeartbeatResponse);
|
||||
NetworkSession.UnRegister(MessageType.EnterRoom, EnterRoomResponse);
|
||||
NetworkSession.UnRegister(MessageType.ExitRoom, ExitRoomResponse);
|
||||
NetworkSession.UnRegister(MessageType.UpdateCameraParam, UpdateCameraParamResponse);
|
||||
NetworkSession.UnRegister(MessageType.MessageSynchronization, MessageSynchronizationResponse);
|
||||
NetworkSession.Close();
|
||||
}
|
||||
|
||||
/// <summary> Executes the 'dis connected' action. </summary>
|
||||
/// <param name="data"> The data.</param>
|
||||
private void OnDisConnected(byte[] data)
|
||||
{
|
||||
OnDisconnect?.Invoke();
|
||||
}
|
||||
|
||||
#region Net msg response
|
||||
/// <summary> Heartbeat response. </summary>
|
||||
/// <param name="data"> The data.</param>
|
||||
private void HeartbeatResponse(byte[] data)
|
||||
{
|
||||
NetworkSession.Received = true;
|
||||
Debug.Log("Receive a heart beat package.");
|
||||
}
|
||||
|
||||
/// <summary> Enter room response. </summary>
|
||||
/// <param name="data"> The data.</param>
|
||||
private void EnterRoomResponse(byte[] data)
|
||||
{
|
||||
EnterRoomData result = SerializerFactory.Create().Deserialize<EnterRoomData>(data);
|
||||
|
||||
if (result.result)
|
||||
{
|
||||
Debug.Log("Join the room success.");
|
||||
OnJoinRoomResult?.Invoke(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Join the room faild.");
|
||||
OnJoinRoomResult?.Invoke(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Exit room response. </summary>
|
||||
/// <param name="data"> The data.</param>
|
||||
private void ExitRoomResponse(byte[] data)
|
||||
{
|
||||
ExitRoomData result = SerializerFactory.Create().Deserialize<ExitRoomData>(data);
|
||||
if (result.Suc)
|
||||
{
|
||||
Debug.Log("Exit the room success.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Exit the room faild.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Updates the camera parameter response described by data. </summary>
|
||||
/// <param name="data"> The data.</param>
|
||||
private void UpdateCameraParamResponse(byte[] data)
|
||||
{
|
||||
CameraParam result = SerializerFactory.Create().Deserialize<CameraParam>(data);
|
||||
OnCameraParamUpdate?.Invoke(result);
|
||||
Debug.Log(result.fov.ToString());
|
||||
}
|
||||
|
||||
private void MessageSynchronizationResponse(byte[] data)
|
||||
{
|
||||
OnMessageResponse?.Invoke(data);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64d6788171afdaf4ea7aa77cd447bdf7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,379 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.XR.XREAL.Samples.NetWork
|
||||
{
|
||||
/// <summary> Call back. </summary>
|
||||
/// <param name="data"> The data.</param>
|
||||
public delegate void CallBack(byte[] data);
|
||||
|
||||
/// <summary> A network session. </summary>
|
||||
public static class NetworkSession
|
||||
{
|
||||
/// <summary> Clent connect state. </summary>
|
||||
private enum ClientState
|
||||
{
|
||||
/// <summary> Disconnect. </summary>
|
||||
None,
|
||||
/// <summary> Connect server success. </summary>
|
||||
Connected,
|
||||
}
|
||||
|
||||
/// <summary> Message type dictionary. </summary>
|
||||
private static Dictionary<MessageType, CallBack> m_CallBacks = new Dictionary<MessageType, CallBack>();
|
||||
/// <summary> Message queue. </summary>
|
||||
private static Queue<byte[]> m_Messages;
|
||||
/// <summary> Client current state. </summary>
|
||||
private static ClientState m_CurState;
|
||||
/// <summary> Gets or sets the current state. </summary>
|
||||
/// <value> The current state. </value>
|
||||
private static ClientState CurState
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_CurState;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_CurState = value;
|
||||
if (m_CurState == ClientState.Connected)
|
||||
{
|
||||
CallBack callback;
|
||||
if (m_CallBacks.TryGetValue(MessageType.Connected, out callback))
|
||||
{
|
||||
callback?.Invoke(null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CallBack callback;
|
||||
if (m_CallBacks.TryGetValue(MessageType.Disconnect, out callback))
|
||||
{
|
||||
callback?.Invoke(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary> The client. </summary>
|
||||
private static TcpClient m_Client;
|
||||
/// <summary> The stream. </summary>
|
||||
private static NetworkStream m_Stream;
|
||||
|
||||
/// <summary> Target address. </summary>
|
||||
private static IPAddress m_Address;
|
||||
/// <summary> The port. </summary>
|
||||
private static int m_Port;
|
||||
|
||||
/// <summary> Heart beat time stamp. </summary>
|
||||
private const float HEARTBEAT_TIME = 1;
|
||||
/// <summary> Time from last heart beat package. </summary>
|
||||
private static float m_Timer = HEARTBEAT_TIME;
|
||||
/// <summary> Get heart beat msg from server. </summary>
|
||||
public static bool Received = true;
|
||||
|
||||
#region coroutines
|
||||
/// <summary> Connects the coroutine. </summary>
|
||||
/// <returns> An IEnumerator. </returns>
|
||||
private static IEnumerator ConnectCoroutine()
|
||||
{
|
||||
m_Client = new TcpClient();
|
||||
|
||||
IAsyncResult async = m_Client.BeginConnect(m_Address, m_Port, null, null);
|
||||
while (!async.IsCompleted)
|
||||
{
|
||||
Debug.Log("Contecting server...");
|
||||
yield return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
m_Client.EndConnect(async);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning("Conncet server faild :" + ex.Message);
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Get data stream
|
||||
try
|
||||
{
|
||||
m_Stream = m_Client.GetStream();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning("Connect server faild:" + ex.Message);
|
||||
yield break;
|
||||
}
|
||||
if (m_Stream == null)
|
||||
{
|
||||
Debug.LogWarning("Connect server faild: data stream is empty");
|
||||
yield break;
|
||||
}
|
||||
|
||||
CurState = ClientState.Connected;
|
||||
m_Messages = new Queue<byte[]>();
|
||||
Debug.Log("Connect server success.");
|
||||
|
||||
// Set asyn msg send
|
||||
NetworkCoroutine.Instance.StartCoroutine(HeartBeat());
|
||||
// Set asyn msg receive
|
||||
NetworkCoroutine.Instance.StartCoroutine(ReceiveCoroutine());
|
||||
// Set quit event
|
||||
NetworkCoroutine.Instance.SetQuitEvent(() => { m_Client.Close(); CurState = ClientState.None; });
|
||||
}
|
||||
|
||||
/// <summary> Heart beat. </summary>
|
||||
/// <returns> An IEnumerator. </returns>
|
||||
private static IEnumerator HeartBeat()
|
||||
{
|
||||
while (CurState == ClientState.Connected)
|
||||
{
|
||||
m_Timer += Time.deltaTime;
|
||||
if (m_Messages.Count > 0)
|
||||
{
|
||||
byte[] data = m_Messages.Dequeue();
|
||||
yield return WriteCoroutine(data);
|
||||
}
|
||||
|
||||
// Heart beat strategy
|
||||
if (m_Timer >= HEARTBEAT_TIME)
|
||||
{
|
||||
// if dont receive last heart beat package.
|
||||
if (!Received)
|
||||
{
|
||||
CurState = ClientState.None;
|
||||
Debug.LogWarning("Heart beat error. disconnect server.");
|
||||
yield break;
|
||||
}
|
||||
m_Timer = 0;
|
||||
byte[] data = Pack(MessageType.HeartBeat);
|
||||
yield return WriteCoroutine(data);
|
||||
|
||||
Debug.Log("Send a heart beat package.");
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Receive coroutine. </summary>
|
||||
/// <returns> An IEnumerator. </returns>
|
||||
private static IEnumerator ReceiveCoroutine()
|
||||
{
|
||||
while (CurState == ClientState.Connected)
|
||||
{
|
||||
byte[] data = new byte[4];
|
||||
|
||||
int length; // msg len
|
||||
MessageType type; // msg type
|
||||
int receive = 0; // receive len
|
||||
|
||||
IAsyncResult async = m_Stream.BeginRead(data, 0, data.Length, null, null);
|
||||
while (!async.IsCompleted)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
receive = m_Stream.EndRead(async);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CurState = ClientState.None;
|
||||
Debug.LogWarning("Receive msg package head erro:" + ex.Message);
|
||||
yield break;
|
||||
}
|
||||
if (receive < data.Length)
|
||||
{
|
||||
CurState = ClientState.None;
|
||||
Debug.LogWarning("Receive msg package head erro");
|
||||
yield break;
|
||||
}
|
||||
|
||||
using (MemoryStream stream = new MemoryStream(data))
|
||||
{
|
||||
BinaryReader binary = new BinaryReader(stream, Encoding.UTF8); // parase data using UTF-8
|
||||
try
|
||||
{
|
||||
length = binary.ReadUInt16();
|
||||
type = (MessageType)binary.ReadUInt16();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
CurState = ClientState.None;
|
||||
Debug.LogWarning("Receive msg package head erro");
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
|
||||
if (length - 4 > 0)
|
||||
{
|
||||
data = new byte[length - 4];
|
||||
async = m_Stream.BeginRead(data, 0, data.Length, null, null);
|
||||
while (!async.IsCompleted)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
receive = m_Stream.EndRead(async);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CurState = ClientState.None;
|
||||
Debug.LogWarning("Receive msg package head erro:" + ex.Message);
|
||||
yield break;
|
||||
}
|
||||
if (receive < data.Length)
|
||||
{
|
||||
CurState = ClientState.None;
|
||||
Debug.LogWarning("Receive msg package head erro");
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data = new byte[0];
|
||||
receive = 0;
|
||||
}
|
||||
|
||||
if (m_CallBacks.ContainsKey(type))
|
||||
{
|
||||
CallBack method = m_CallBacks[type];
|
||||
method(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Did not regist the msg callback : " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Writes a coroutine. </summary>
|
||||
/// <param name="data"> The data.</param>
|
||||
/// <returns> An IEnumerator. </returns>
|
||||
private static IEnumerator WriteCoroutine(byte[] data)
|
||||
{
|
||||
if (CurState != ClientState.Connected || m_Stream == null)
|
||||
{
|
||||
Debug.LogWarning("Connect error, can not receive msg");
|
||||
yield break;
|
||||
}
|
||||
|
||||
IAsyncResult async = m_Stream.BeginWrite(data, 0, data.Length, null, null);
|
||||
while (!async.IsCompleted)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
m_Stream.EndWrite(async);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CurState = ClientState.None;
|
||||
Debug.LogWarning("Send msg erro:" + ex.Message);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary> Connect server. </summary>
|
||||
/// <param name="address"> (Optional) The address.</param>
|
||||
/// <param name="port"> (Optional) The port.</param>
|
||||
public static void Connect(string address = null, int port = 8848)
|
||||
{
|
||||
// Can not connect again after connected.
|
||||
if (CurState == ClientState.Connected)
|
||||
{
|
||||
Debug.Log("Has connected server.");
|
||||
return;
|
||||
}
|
||||
if (address == null)
|
||||
address = NetworkUtils.GetLocalIPv4();
|
||||
|
||||
// Cancle when get the ipaddress failed.
|
||||
if (!IPAddress.TryParse(address, out m_Address))
|
||||
{
|
||||
Debug.LogWarning("IP erro, try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_Port = port;
|
||||
// Connect service.
|
||||
NetworkCoroutine.Instance.StartCoroutine(ConnectCoroutine());
|
||||
}
|
||||
|
||||
/// <summary> Closes this object. </summary>
|
||||
public static void Close()
|
||||
{
|
||||
if (CurState == ClientState.Connected)
|
||||
{
|
||||
m_Client.Close();
|
||||
m_CurState = ClientState.None;
|
||||
}
|
||||
NetworkCoroutine.Instance.StopAllCoroutines();
|
||||
}
|
||||
|
||||
/// <summary> Regist callback event. </summary>
|
||||
/// <param name="type"> The type.</param>
|
||||
/// <param name="method"> The method.</param>
|
||||
public static void Register(MessageType type, CallBack method)
|
||||
{
|
||||
if (!m_CallBacks.ContainsKey(type))
|
||||
m_CallBacks.Add(type, method);
|
||||
else
|
||||
Debug.LogWarning("Regist the same msg type.");
|
||||
}
|
||||
|
||||
/// <summary> Un register. </summary>
|
||||
/// <param name="type"> The type.</param>
|
||||
/// <param name="method"> The method.</param>
|
||||
public static void UnRegister(MessageType type, CallBack method)
|
||||
{
|
||||
if (m_CallBacks.ContainsKey(type))
|
||||
{
|
||||
m_CallBacks.Remove(type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Join the msg queue. </summary>
|
||||
/// <param name="type"> The type.</param>
|
||||
/// <param name="data"> (Optional) The data.</param>
|
||||
public static void Enqueue(MessageType type, byte[] data = null)
|
||||
{
|
||||
// Pack the data
|
||||
byte[] bytes = Pack(type, data);
|
||||
|
||||
if (CurState == ClientState.Connected)
|
||||
{
|
||||
m_Messages.Enqueue(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Pack the byte data. </summary>
|
||||
/// <param name="type"> The type.</param>
|
||||
/// <param name="data"> (Optional) The data.</param>
|
||||
/// <returns> A byte[]. </returns>
|
||||
private static byte[] Pack(MessageType type, byte[] data = null)
|
||||
{
|
||||
MessagePacker packer = new MessagePacker();
|
||||
if (data != null)
|
||||
{
|
||||
packer.Add((ushort)(4 + data.Length)); // msg length
|
||||
packer.Add((ushort)type); // msg type
|
||||
packer.Add(data); // msg content
|
||||
}
|
||||
else
|
||||
{
|
||||
packer.Add(4);
|
||||
packer.Add((ushort)type);
|
||||
}
|
||||
return packer.Package;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 81d4af6fac544cc498693b34606a6330
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e157ed67b5af0f74ba3dcbc64fce2cee
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.XR.XREAL.Samples.NetWork
|
||||
{
|
||||
/// <summary> A message packer. </summary>
|
||||
public class MessagePacker
|
||||
{
|
||||
/// <summary> The bytes. </summary>
|
||||
private List<byte> bytes = new List<byte>();
|
||||
|
||||
/// <summary> Gets the package. </summary>
|
||||
/// <value> The package. </value>
|
||||
public byte[] Package
|
||||
{
|
||||
get { return bytes.ToArray(); }
|
||||
}
|
||||
|
||||
/// <summary> Adds value. </summary>
|
||||
/// <param name="data"> The data to add.</param>
|
||||
/// <returns> A MessagePacker. </returns>
|
||||
public MessagePacker Add(byte[] data)
|
||||
{
|
||||
bytes.AddRange(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary> Adds value. </summary>
|
||||
/// <param name="value"> The value to add.</param>
|
||||
/// <returns> A MessagePacker. </returns>
|
||||
public MessagePacker Add(ushort value)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
bytes.AddRange(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary> Adds value. </summary>
|
||||
/// <param name="value"> The value to add.</param>
|
||||
/// <returns> A MessagePacker. </returns>
|
||||
public MessagePacker Add(uint value)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
bytes.AddRange(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary> Adds value. </summary>
|
||||
/// <param name="value"> The value to add.</param>
|
||||
/// <returns> A MessagePacker. </returns>
|
||||
public MessagePacker Add(ulong value)
|
||||
{
|
||||
byte[] data = BitConverter.GetBytes(value);
|
||||
bytes.AddRange(data);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3ad4b2ef850781438a4531d3c8843d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.XR.XREAL.Samples.NetWork
|
||||
{
|
||||
/// <summary> A network coroutine. </summary>
|
||||
internal class NetworkCoroutine : MonoBehaviour
|
||||
{
|
||||
/// <summary> Event queue for all listeners interested in applicationQuit events. </summary>
|
||||
private event Action ApplicationQuitEvent;
|
||||
|
||||
/// <summary> The instance. </summary>
|
||||
private static NetworkCoroutine _instance;
|
||||
|
||||
/// <summary> Gets the instance. </summary>
|
||||
/// <value> The instance. </value>
|
||||
public static NetworkCoroutine Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!_instance)
|
||||
{
|
||||
GameObject socketClientObj = new GameObject("NetworkCoroutine");
|
||||
_instance = socketClientObj.AddComponent<NetworkCoroutine>();
|
||||
DontDestroyOnLoad(socketClientObj);
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Sets quit event. </summary>
|
||||
/// <param name="func"> The function.</param>
|
||||
public void SetQuitEvent(Action func)
|
||||
{
|
||||
if (ApplicationQuitEvent != null) return;
|
||||
ApplicationQuitEvent += func;
|
||||
}
|
||||
|
||||
/// <summary> Executes the 'application quit' action. </summary>
|
||||
private void OnApplicationQuit()
|
||||
{
|
||||
ApplicationQuitEvent?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 239487c972f53a549a19b8fe475f3610
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,135 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.XR.XREAL.Samples.NetWork
|
||||
{
|
||||
/// <summary> A network utilities. </summary>
|
||||
public static class NetworkUtils
|
||||
{
|
||||
/// <summary> Get local ipv4, return null if faild. </summary>
|
||||
/// <returns> The local IPv4. </returns>
|
||||
public static string GetLocalIPv4()
|
||||
{
|
||||
string hostName = Dns.GetHostName(); //得到主机名
|
||||
IPHostEntry iPEntry = Dns.GetHostEntry(hostName);
|
||||
for (int i = 0; i < iPEntry.AddressList.Length; i++)
|
||||
{
|
||||
//从IP地址列表中筛选出IPv4类型的IP地址
|
||||
if (iPEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
|
||||
return iPEntry.AddressList[i].ToString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary> Byte 2 string. </summary>
|
||||
/// <param name="bytes"> The bytes.</param>
|
||||
/// <returns> A string. </returns>
|
||||
public static string Byte2String(byte[] bytes)
|
||||
{
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
/// <summary> String 2 byte. </summary>
|
||||
/// <param name="str"> The string.</param>
|
||||
/// <returns> A byte[]. </returns>
|
||||
public static byte[] String2Byte(string str)
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(str);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Interface for serializer. </summary>
|
||||
public interface ISerializer
|
||||
{
|
||||
/// <summary> Serialize this object to the given stream. </summary>
|
||||
/// <param name="obj"> The object.</param>
|
||||
/// <returns> A byte[]. </returns>
|
||||
byte[] Serialize(object obj);
|
||||
|
||||
/// <summary> Deserialize this object to the given stream. </summary>
|
||||
/// <typeparam name="T"> Generic type parameter.</typeparam>
|
||||
/// <param name="data"> The data.</param>
|
||||
/// <returns> A T. </returns>
|
||||
T Deserialize<T>(byte[] data) where T : class;
|
||||
}
|
||||
|
||||
/// <summary> An object for persisting JSON data. </summary>
|
||||
public class JsonSerializer : ISerializer
|
||||
{
|
||||
/// <summary> Deserialize this object to the given stream. </summary>
|
||||
/// <typeparam name="T"> Generic type parameter.</typeparam>
|
||||
/// <param name="data"> The data.</param>
|
||||
/// <returns> A T. </returns>
|
||||
public T Deserialize<T>(byte[] data) where T : class
|
||||
{
|
||||
return LitJson.JsonMapper.ToObject<T>(Encoding.UTF8.GetString(data));
|
||||
}
|
||||
|
||||
/// <summary> Serialize this object to the given stream. </summary>
|
||||
/// <param name="obj"> The object.</param>
|
||||
/// <returns> A byte[]. </returns>
|
||||
public byte[] Serialize(object obj)
|
||||
{
|
||||
return Encoding.UTF8.GetBytes(LitJson.JsonMapper.ToJson(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> An object for persisting binary data. </summary>
|
||||
public class BinarySerializer : ISerializer
|
||||
{
|
||||
/// <summary> obj -> bytes, return null if obj not mark as [Serializable]. </summary>
|
||||
/// <param name="obj"> The object.</param>
|
||||
/// <returns> A byte[]. </returns>
|
||||
public byte[] Serialize(object obj)
|
||||
{
|
||||
//物体不为空且可被序列化
|
||||
if (obj == null || !obj.GetType().IsSerializable)
|
||||
return null;
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
formatter.Serialize(stream, obj);
|
||||
byte[] data = stream.ToArray();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> bytes -> obj, return null if obj not mark as [Serializable]. </summary>
|
||||
/// <typeparam name="T"> Generic type parameter.</typeparam>
|
||||
/// <param name="data"> The data.</param>
|
||||
/// <returns> A T. </returns>
|
||||
public T Deserialize<T>(byte[] data) where T : class
|
||||
{
|
||||
//数据不为空且T是可序列化的类型
|
||||
if (data == null || !typeof(T).IsSerializable)
|
||||
return null;
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
using (MemoryStream stream = new MemoryStream(data))
|
||||
{
|
||||
object obj = formatter.Deserialize(stream);
|
||||
return obj as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> A serializer factory. </summary>
|
||||
public static class SerializerFactory
|
||||
{
|
||||
/// <summary> The serializer. </summary>
|
||||
private static ISerializer _Serializer;
|
||||
|
||||
/// <summary> Creates a new ISerializer. </summary>
|
||||
/// <returns> An ISerializer. </returns>
|
||||
public static ISerializer Create()
|
||||
{
|
||||
if (_Serializer == null)
|
||||
{
|
||||
_Serializer = new JsonSerializer();
|
||||
}
|
||||
return _Serializer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b987fb1d08c23e4085e3f56c50c9b00
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user