969 lines
35 KiB
C#
969 lines
35 KiB
C#
using System;
|
||
using System.IO;
|
||
using System.Collections.Generic;
|
||
|
||
using System.Text;
|
||
using System.Net.Sockets;
|
||
using ProtoBuf;
|
||
using Thousandto.Core.Base;
|
||
using Thousandto.Core.Framework;
|
||
using System.Threading;
|
||
using Thousandto.Core.Support;
|
||
using Thousandto.Code.Logic.Network;
|
||
using PathUtils = UnityEngine.Gonbest.MagicCube.PathUtils;
|
||
using CoroutinePool = UnityEngine.Gonbest.MagicCube.CoroutinePool;
|
||
using StringUtils = UnityEngine.Gonbest.MagicCube.StringUtils;
|
||
|
||
|
||
namespace Thousandto.Plugins.Common
|
||
{
|
||
public delegate void DoResMessageDelegate(uint msgid, byte[] bytes);
|
||
/// <summary>
|
||
/// socket管理类,主要处理发送数据的打包以及对收到的数据调用处理函数
|
||
/// 重要方法SendMessage 、 update
|
||
///
|
||
/// </summary>
|
||
public class Networker : BaseSystem
|
||
{
|
||
public MyAction<float> OnUpdateFunc;
|
||
public float RecordMsgTagTime = 0;
|
||
|
||
private MyAction<int> _onDisconnnect;
|
||
|
||
//回调函数队列
|
||
private SyncQueue<KeyValuePair<MyAction<bool>, bool>> _calbackQueue = new SyncQueue<KeyValuePair<MyAction<bool>, bool>>();
|
||
|
||
//lua消息响应事件
|
||
public DoResMessageDelegate ResLuaMessageEvent = (msgid,bytes) => { };
|
||
|
||
public static List<byte[]> GetAllProtoPath()
|
||
{
|
||
return Thousandto.Code.Logic.LuaProtoBuff.GetAllProtoPath();
|
||
}
|
||
|
||
public MyAction<int> OnSocketDisconnect
|
||
{
|
||
get
|
||
{
|
||
return _onDisconnnect;
|
||
}
|
||
set
|
||
{
|
||
_onDisconnnect = value;
|
||
if (_sockeClient != null)
|
||
{
|
||
_sockeClient.OnSocketDisconnect = x => { if (_onDisconnnect != null) _onDisconnnect(x); };
|
||
}
|
||
}
|
||
}
|
||
|
||
#region 成员变量
|
||
// default vars in different platform
|
||
#if (UNITY_IPHONE || UNITY_ANDROID) && !UNITY_EDITOR
|
||
const float MaxMsgBlockingTime = 0.1f;
|
||
const int MaxMsgBlockingFrame = 5;
|
||
#else
|
||
//每隔一定时间来处理一条消息
|
||
private const float MaxMsgBlockingTime = 5.0f;
|
||
private const int MaxMsgBlockingFrame = 60;
|
||
#endif
|
||
//发消息需要记录发送消息的个数,与服务器做一一对应
|
||
private static Object lockSendCounter = new Object();
|
||
private static int msgSendCounter = 0;
|
||
private static System.Random random = new Random();
|
||
//发送缓存最大为100k
|
||
private static int BYTE_BUFFER_SIZE = 1024 * 100;
|
||
// connecting / connected -- IP, Port
|
||
public string IP = "124.220.47.41";
|
||
public int Port = 44801;
|
||
|
||
//用于间隔MaxMsgBlockingTime这个时间来处理一条消息
|
||
private float _msgBlockingTime = 0;
|
||
private int _msgBlockingFrame = 0;
|
||
|
||
|
||
//跳帧处理消息
|
||
private int _msgLoopSkipCount = 0;
|
||
private float _networkDelayCount = 0;
|
||
|
||
private Byte[] _serializeBuffer = null;
|
||
|
||
//发送消息的内存流,可以复用
|
||
private MemoryStream _serializeStream = null;
|
||
|
||
//系列化消息的内存流,可以复用
|
||
private static MemoryStream _serializeMsgStream = null;
|
||
|
||
private BinaryWriter _binaryWriter = null;
|
||
|
||
private SocketClient _sockeClient = null;
|
||
|
||
private Thread _thread;
|
||
private bool _enableThread;
|
||
|
||
#endregion
|
||
|
||
#region get set方法
|
||
static Networker _sharedInstance = null;
|
||
|
||
public static Networker Instance
|
||
{
|
||
get
|
||
{
|
||
return SharedInstance;
|
||
}
|
||
}
|
||
|
||
public static Networker SharedInstance
|
||
{
|
||
get
|
||
{
|
||
if (_sharedInstance == null)
|
||
{
|
||
_sharedInstance = new Networker();
|
||
}
|
||
return _sharedInstance;
|
||
}
|
||
}
|
||
|
||
public bool IsConnected
|
||
{
|
||
get { return _sockeClient != null && _sockeClient.IsConnected; }
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region socket连接和断开
|
||
public void Connect(MyAction<bool> requestCallback = null)
|
||
{
|
||
_msgLoopSkipCount = 0;
|
||
if (_sockeClient != null)
|
||
{
|
||
System.Diagnostics.Trace.Write(string.Format("begin connect ip:[{0}] port[{1}]", IP, Port));
|
||
|
||
_sockeClient.Connect((result) =>
|
||
{
|
||
System.Diagnostics.Trace.Write(string.Format("connect ip:[{0}] port[{1}] result[{2}]", IP, Port, result));
|
||
|
||
if (result)
|
||
{
|
||
ReInitSocketCounter();
|
||
_sockeClient.EnableReceive(true);
|
||
}
|
||
else
|
||
{
|
||
_sockeClient.EnableReceive(false);
|
||
}
|
||
|
||
_calbackQueue.Enqueue(new KeyValuePair<MyAction<bool>, bool>(requestCallback, result));
|
||
//if (requestCallback != null)
|
||
//{
|
||
// requestCallback(result);
|
||
//}
|
||
|
||
}, IP, Port);
|
||
}
|
||
}
|
||
|
||
public void Disconnect(bool notify = true)
|
||
{
|
||
System.Diagnostics.Trace.Write("Disconnect");
|
||
|
||
if (_sockeClient != null)
|
||
{
|
||
_sockeClient.Close(notify);
|
||
}
|
||
}
|
||
|
||
public void TextCloseForReconnect()
|
||
{
|
||
if (_sockeClient != null)
|
||
_sockeClient.TextCloseForReconnect();
|
||
}
|
||
|
||
#endregion
|
||
|
||
public void Initialize()
|
||
{
|
||
_msgLoopSkipCount = 0;
|
||
_serializeBuffer = new Byte[BYTE_BUFFER_SIZE];
|
||
_serializeStream = new MemoryStream(_serializeBuffer);
|
||
_binaryWriter = new FastBinaryWriter(_serializeStream);
|
||
_sockeClient = new SocketClient();
|
||
_sockeClient.Initialize();
|
||
_enableThread = false;
|
||
|
||
//心跳消息和断线重连消息需要在子线程做处理
|
||
_sockeClient.SetThreadHandelMsgID((int)MSG_heart.ResHeart.MsgID);
|
||
_sockeClient.SetThreadHandelMsgID((int)MSG_heart.ResReconnectSign.MsgID);
|
||
|
||
//优先处理的消息,比如伤害包
|
||
//_sockeClient.SetHighPriorityHandleMsgID((int)MSG_Fight.ResAttackResult.MsgID.eMsgID);
|
||
|
||
//startThread();
|
||
}
|
||
|
||
public void Uninitialize()
|
||
{
|
||
if (_sockeClient != null)
|
||
{
|
||
_sockeClient.Uninitialize();
|
||
//_sockeClient = null;
|
||
}
|
||
StopThread();
|
||
|
||
if (_serializeMsgStream != null)
|
||
{
|
||
_serializeMsgStream.Dispose();
|
||
_serializeMsgStream = null;
|
||
}
|
||
}
|
||
|
||
public void SkipMessageLoop(int count = 1)
|
||
{
|
||
_msgLoopSkipCount += count;
|
||
}
|
||
|
||
public int GetSkipCount()
|
||
{
|
||
return _msgLoopSkipCount;
|
||
}
|
||
|
||
public void Send(byte[] mm, uint msgID)
|
||
{
|
||
//UnityEngine.Debug.LogWarning("[new发送消息:" + msgID + "] " + GetStr(mm));
|
||
if (GameGlobalData.IsReconnecting && msgID != MSG_heart.ReqReconnect.MsgID)
|
||
{
|
||
System.Diagnostics.Trace.Fail("当前是断线重连, 普通消息抛弃掉: " + msgID);
|
||
return;
|
||
}
|
||
|
||
if (_thread == null || _thread.IsAlive == false)
|
||
{
|
||
System.Diagnostics.Trace.Fail(string.Format("msg id[{0}] 消息线程挂了", msgID));
|
||
}
|
||
if (_sockeClient == null || _sockeClient.IsConnected == false)
|
||
{
|
||
System.Diagnostics.Trace.Fail(string.Format("msg id[{0}] 网络已经断开", msgID));
|
||
}
|
||
|
||
if (msgID > 0)
|
||
{
|
||
try
|
||
{
|
||
NetworkpackageProfiler.PushTimestampData((uint)msgID, NetworkPackageTimestamp.MsgType.Send);
|
||
int curMsgSendCounter = 0;
|
||
// SendMessage可能多线程调用,需要加锁
|
||
lock (lockSendCounter)
|
||
{
|
||
curMsgSendCounter = msgSendCounter;
|
||
msgSendCounter++;
|
||
|
||
MemoryStream m = _serializeStream;
|
||
m.SetLength(0);
|
||
m.Position = 0;
|
||
BinaryWriter s = _binaryWriter;
|
||
UInt32 size = 0;
|
||
UInt32 tempId = 0;
|
||
UInt32 timeNow = (UInt32)TimeUtils.GetNow();
|
||
UInt32 id = (UInt32)msgID;
|
||
|
||
size = (UInt32)mm.Length;
|
||
|
||
tempId = (UInt32)curMsgSendCounter ^ (0x6B << 10);
|
||
tempId = tempId ^ (16 + size);
|
||
|
||
s.Write(MessageData.convertToBigInt(size + 16));
|
||
s.Write(MessageData.convertToBigInt(tempId));
|
||
|
||
uint ext = tempId % 100;
|
||
byte[] timeBytes = MessageData.convertToBigInt(timeNow);
|
||
byte[] idBytes = MessageData.convertToBigInt(id);
|
||
|
||
int total = sumTotal(timeBytes) + sumTotal(idBytes) + sumTotal(mm);
|
||
s.Write(MessageData.convertToBigInt((uint)total));
|
||
|
||
s.Write(xor(timeBytes, ext));
|
||
s.Write(xor(idBytes, ext));
|
||
s.Write(xor(mm, ext));
|
||
|
||
byte[] dataByte = new byte[size + 4 * 5];
|
||
|
||
Buffer.BlockCopy(_serializeBuffer, 0, dataByte, 0, dataByte.Length);
|
||
_sockeClient.PostMessage(dataByte);
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
System.Diagnostics.Trace.Fail(e.Message + "\n" + e.StackTrace);
|
||
}
|
||
}
|
||
ProtoBufUtils.Free(mm);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 结构--> |消息大小+12|消息计数器|时间戳|消息id|消息体|
|
||
/// 消息发送有改动,新增一个消息ascii值的和作为一位,时间戳、消息id、消息体与计数器余数异或
|
||
/// 新结构--> |消息大小+16|消息计数器|ascii和|时间戳每位与余数异或|消息id每位与余数异或|消息体每位与余数异或|
|
||
/// </summary>
|
||
/// <param name="msg"></param>
|
||
/// <param name="msgId"></param>
|
||
public void SendMessage(ProtoBuf.IExtensible msg, int msgID)
|
||
{
|
||
//UnityEngine.Debug.LogWarning("[old发送消息:" + msgID + "] " + GetStr(Serialize(msg)));
|
||
}
|
||
|
||
//异或
|
||
private byte[] xor(byte[] value, uint arg)
|
||
{
|
||
for (int i = 0; i < value.Length; ++i)
|
||
{
|
||
value[i] = (byte)(value[i] ^ arg);
|
||
}
|
||
|
||
return value;
|
||
}
|
||
|
||
//求和
|
||
private int sumTotal(byte[] value)
|
||
{
|
||
int total = 0;
|
||
|
||
for (int i = 0; i < value.Length; ++i)
|
||
{
|
||
total += value[i];
|
||
}
|
||
|
||
return total;
|
||
}
|
||
|
||
public void ReflushSendQueue()
|
||
{
|
||
_sockeClient.ReflushSendQueue();
|
||
}
|
||
|
||
public void StopThread()
|
||
{
|
||
_enableThread = false;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 启动一个子线程,用来发送消息和处理需要在子线程里面处理的消息,比如心跳消息
|
||
/// </summary>
|
||
public void StartThread()
|
||
{
|
||
if (_enableThread == true)
|
||
{
|
||
return;
|
||
}
|
||
_enableThread = true;
|
||
_thread = new Thread(new ThreadStart(threadFunc));
|
||
_thread.IsBackground = true;
|
||
_thread.Start();
|
||
}
|
||
|
||
//多线程协议处理
|
||
private void threadFunc()
|
||
{
|
||
DateTime nowTime = DateTime.Now;
|
||
|
||
int nCurThreadID = Thread.CurrentThread.ManagedThreadId;
|
||
System.Diagnostics.Trace.Write(string.Format("Networker threadFunc start nowTime[{0}] nCurThreadID[{1}]", nowTime, nCurThreadID));
|
||
|
||
while (_enableThread && _sockeClient != null)
|
||
{
|
||
nowTime = System.DateTime.Now;
|
||
Thread.Sleep(1);
|
||
|
||
//peek需要在子线程处理的消息,比如心跳和断线重连消息
|
||
MessageData data = _sockeClient.PopMessageHandelInThread();
|
||
if (data != null)
|
||
{
|
||
try
|
||
{
|
||
//通过消息ID找到反序列化改消息的方法,主要是不同消息的类不同,需要不同的反序列化方法,统一不了
|
||
//IExtensible msg = HandleMsg.DeserializeMsgById((int)data.MsgID, data.Data, (int)data.DataSize);
|
||
IExtensible msg = ProtoBuf.Serializers.CustomSetting.TryGetDeserializer(data.MsgID);
|
||
if (msg != null)
|
||
{
|
||
IResMessage res = msg as IResMessage;
|
||
res.ReadMessage(data.Data);
|
||
msg.Excute();
|
||
}
|
||
else
|
||
{
|
||
System.Diagnostics.Trace.Fail(string.Format("消息解析有问题,msg=null, id={0}, dataLen={1}", data.MsgID, data.Data == null ? 0 : data.Data.Length));
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Trace.Fail(string.Format("Excute message DataSize[{0}] id={1},throw exception:{2}", data.DataSize, data.MsgID, ex.Message));
|
||
System.Diagnostics.Trace.Fail(ex.Message + "\n" + ex.StackTrace);
|
||
|
||
StringBuilder sb = new StringBuilder();
|
||
for (int i = 0; i < data.Data.Length; ++i)
|
||
{
|
||
sb.Append(data.Data[i]);
|
||
sb.Append(" ");
|
||
}
|
||
System.Diagnostics.Trace.Fail(sb.ToString());
|
||
}
|
||
finally
|
||
{
|
||
MessageData.FreeMsgData(data);
|
||
}
|
||
}
|
||
|
||
//消息发送放到了后台线程来做
|
||
_sockeClient.ReflushSendQueue();
|
||
|
||
//心跳发送倒计时
|
||
if (OnUpdateFunc != null)
|
||
{
|
||
OnUpdateFunc((int)(DateTime.Now - nowTime).TotalMilliseconds);
|
||
}
|
||
}
|
||
|
||
nowTime = DateTime.Now;
|
||
System.Diagnostics.Trace.Write(string.Format("Networker threadFunc end nowTime[{0}] nCurThreadID[{1}]", nowTime, nCurThreadID));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 这里主要是添加心跳处理函数,发送心跳消息用
|
||
/// </summary>
|
||
/// <param name="func"></param>
|
||
public void AddThreadHandleFunc(MyAction<float> func)
|
||
{
|
||
OnUpdateFunc = func;
|
||
}
|
||
|
||
|
||
protected override bool OnUpdate(float deltaTime)
|
||
{
|
||
//处理回调方法
|
||
if (_calbackQueue.Count > 0)
|
||
{
|
||
var callback = _calbackQueue.Dequeue();
|
||
if (callback.Key != null)
|
||
callback.Key(callback.Value);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理消息队列
|
||
/// </summary>
|
||
/// <param name="deltaTime"></param>
|
||
public void FixedUpdate(float deltaTime)
|
||
{
|
||
// 已处理完成的消息池需要定时回收内存
|
||
//MessageData.Update(deltaTime);
|
||
if (_sockeClient == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (_sockeClient.IsConnected)
|
||
{
|
||
// 跳帧
|
||
if (_msgLoopSkipCount > 0)
|
||
{
|
||
--_msgLoopSkipCount;
|
||
return;
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
bool debugMsg = false;
|
||
int unHandleMsgCount = _sockeClient.ReceiveNormalMsgCount();
|
||
//待处理的消息超过60个,会造成明显卡顿,这里要给条日志,告知服务器消息太多了
|
||
if (unHandleMsgCount > 60)
|
||
{
|
||
//UnityEngine.Debug.LogError("Too many message receive in one frame, cout=" + unHandleMsgCount);
|
||
debugMsg = true;
|
||
}
|
||
#endif
|
||
|
||
//如果接收到的消息超过60个还没有被处理,说明游戏从后台切到前台,积累的很多消息没处理
|
||
//这里就在1帧里集中处理
|
||
do
|
||
{
|
||
MessageData data = _sockeClient.PopNormalMessage();
|
||
if (data != null)
|
||
{
|
||
try
|
||
{
|
||
//if(data.DataSize > 512)
|
||
// UnityEngine.Debug.LogError("MsgID = " + data.MsgID + ", Size = " + data.DataSize);
|
||
//lock (_lockObj)
|
||
{
|
||
if (MsgExtend.SharedInstance.IsGoLuaMsg(data.MsgID))
|
||
{
|
||
//执行纯lua消息
|
||
//如果Client\Main\Assets\GameAssets\Resources\Lua\Network\ResMsgCMD.Lua 中有该CMD,或者ID大于500000,走Lua端消息
|
||
UnityEngine.Profiling.Profiler.BeginSample("Lua Process Message:" + data.MsgID);
|
||
ResLuaMessageEvent(data.MsgID, data.Data);
|
||
UnityEngine.Profiling.Profiler.EndSample();
|
||
continue;
|
||
}
|
||
if (MsgExtend.SharedInstance.IsGoLuaExtendMsg(data.MsgID))
|
||
{
|
||
//执行lua和C#同时处理的消息
|
||
UnityEngine.Profiling.Profiler.BeginSample("Lua Process Extend Message:" + data.MsgID);
|
||
ResLuaMessageEvent(data.MsgID, data.Data);
|
||
UnityEngine.Profiling.Profiler.EndSample();
|
||
}
|
||
//UnityEngine.Profiling.Profiler.BeginSample("====================== Networker cs: "+ data.MsgID);
|
||
//通过消息ID找到反序列化改消息的方法,主要是不同消息的类不同,需要不同的反序列化方法,统一不了
|
||
//IExtensible msg = HandleMsg.DeserializeMsgById((int)data.MsgID, data.Data, (int)data.DataSize);
|
||
IExtensible msg = ProtoBuf.Serializers.CustomSetting.TryGetDeserializer(data.MsgID);
|
||
if (msg != null)
|
||
{
|
||
IResMessage res = msg as IResMessage;
|
||
res.ReadMessage(data.Data);
|
||
#if UNITY_EDITOR
|
||
//if (UnityEngine.Time.realtimeSinceStartup - RecordMsgTagTime < 10)
|
||
//{
|
||
// UnityEngine.Debug.Log("Reconnect: receive msg - " + "id=" + data.MsgID + "--" + msg);
|
||
//}
|
||
var startTime = DateTime.Now;
|
||
NetworkpackageProfiler.PushTimestampData(data.MsgID, NetworkPackageTimestamp.MsgType.Receive);
|
||
if (debugMsg)
|
||
{
|
||
//UnityEngine.Debug.LogError("HandMsg =" + msg.ToString());
|
||
}
|
||
#endif
|
||
if (TestMessage)
|
||
{
|
||
JsonSerialize.WriteTestLog(msg);
|
||
}
|
||
else
|
||
JsonSerialize.Stop();
|
||
#if ENABLE_PROFILER
|
||
//UnityEngine.Profiler.BeginSample("msg.Excute:" + msg.GetType().Name);
|
||
#endif
|
||
|
||
msg.Excute();
|
||
#if ENABLE_PROFILER
|
||
//UnityEngine.Profiler.EndSample();
|
||
#endif
|
||
|
||
MessageData.FreeMsgData(data);
|
||
#if UNITY_EDITOR
|
||
var endTime = DateTime.Now;
|
||
NetworkpackageProfiler.PushTimeElapsed(data.MsgID, (endTime - startTime).TotalMilliseconds);
|
||
#endif
|
||
//UnityEngine.Profiling.Profiler.EndSample();
|
||
}
|
||
else
|
||
{
|
||
System.Diagnostics.Trace.Fail(string.Format("消息解析有问题,msg=null, id={0}, dataLen={1}", data.MsgID, data.Data == null ? 0 : data.Data.Length));
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Trace.Fail(string.Format("Excute message DataSize[{0}] id={1},throw exception:{2}", data.DataSize, data.MsgID, ex.Message));
|
||
System.Diagnostics.Trace.Fail(ex.Message + "\n" + ex.StackTrace);
|
||
|
||
StringBuilder sb = new StringBuilder();
|
||
for (int i = 0; i < data.Data.Length; ++i)
|
||
{
|
||
sb.Append(data.Data[i]);
|
||
sb.Append(" ");
|
||
}
|
||
|
||
System.Diagnostics.Trace.Fail(sb.ToString());
|
||
}
|
||
}
|
||
} while (_sockeClient.ReceiveNormalMsgCount() > 0);
|
||
}
|
||
}
|
||
|
||
private string GetStr(byte[] bytes)
|
||
{
|
||
if (bytes.Length > 0)
|
||
{
|
||
StringBuilder sb = new StringBuilder();
|
||
for (int i = 0; i < bytes.Length; i++)
|
||
{
|
||
sb.Append(bytes[i]);
|
||
sb.Append(" ");
|
||
}
|
||
return sb.ToString();
|
||
}
|
||
return "";
|
||
}
|
||
|
||
public int GetSocketErrorCode()
|
||
{
|
||
return _sockeClient.GetSocketErrorCode();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重置消息数,当socket断开或者重连都需要调用
|
||
/// </summary>
|
||
public static void ReInitSocketCounter()
|
||
{
|
||
msgSendCounter = 0;
|
||
}
|
||
|
||
//从连接开始计数,总共发送消息的个数
|
||
public static int GetMsgCount()
|
||
{
|
||
return msgSendCounter;
|
||
}
|
||
|
||
////请优化@喻强
|
||
///// <summary>
|
||
///// 消息序列化
|
||
///// </summary>
|
||
///// <param name="msg"></param>
|
||
///// <returns></returns>
|
||
//public static byte[] Serialize(IExtensible msg)
|
||
//{
|
||
// byte[] result;
|
||
// //这里new不能优化,不能清空stream的内存
|
||
// // using (var stream = new MemoryStream())
|
||
// // {
|
||
// // Serializer.Serialize(stream, msg);
|
||
// // result = stream.ToArray();
|
||
// // }
|
||
|
||
// if (_serializeMsgStream == null)
|
||
// {
|
||
// _serializeMsgStream = new MemoryStream();
|
||
// }
|
||
|
||
// //设置为0后就可以清空内存,该对象就可以复用
|
||
// _serializeMsgStream.SetLength(0);
|
||
// Serializer.Serialize(_serializeMsgStream, msg);
|
||
// result = _serializeMsgStream.ToArray();
|
||
|
||
// return result;
|
||
//}
|
||
|
||
/// <summary>
|
||
/// 反序列化
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="message"></param>
|
||
/// <returns></returns>
|
||
//public static T Deserialize<T>(byte[] message)
|
||
//{
|
||
// T result;
|
||
// using (var stream = new MemoryStream(message))
|
||
// {
|
||
// result = Serializer.Deserialize<T>(stream);
|
||
// }
|
||
// return result;
|
||
//}
|
||
|
||
|
||
#region 测试用的,QA用来白盒测试消息数据是否正确
|
||
|
||
//开关
|
||
public static bool TestMessage;
|
||
|
||
private class JsonSerialize
|
||
{
|
||
private static string _logPath = "Log/";
|
||
private static StreamWriter _sw;
|
||
private static SyncQueue<string> _logQueue;
|
||
private static bool _init;
|
||
private static Thread _thread;
|
||
|
||
public static void WriteTestLog(object msg)
|
||
{
|
||
if (_sw == null)
|
||
{
|
||
_init = true;
|
||
string dirName = _logPath;
|
||
if (!Directory.Exists(dirName))
|
||
{
|
||
Directory.CreateDirectory(dirName);
|
||
}
|
||
_sw = new StreamWriter(dirName + "/" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + "_MsgLog.txt");
|
||
_logQueue = new SyncQueue<string>();
|
||
|
||
_thread = new Thread(writeThread);
|
||
_thread.IsBackground = true;
|
||
_thread.Start();
|
||
}
|
||
|
||
string json = ToJson(msg);
|
||
_logQueue.Enqueue(json);
|
||
UnityEngine.Debug.LogError(json);
|
||
}
|
||
|
||
public static void Stop()
|
||
{
|
||
if (!_init)
|
||
{
|
||
return;
|
||
}
|
||
|
||
_init = false;
|
||
|
||
if (_logQueue != null)
|
||
{
|
||
_logQueue.Clear();
|
||
_logQueue = new SyncQueue<string>();
|
||
}
|
||
|
||
if (_thread != null)
|
||
{
|
||
_thread.Join(1000);
|
||
}
|
||
|
||
_sw = null;
|
||
}
|
||
|
||
private static void writeThread()
|
||
{
|
||
while (_init)
|
||
{
|
||
Thread.Sleep(1);
|
||
if (_logQueue.Count > 0)
|
||
{
|
||
string line = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss -> ") + _logQueue.Dequeue();
|
||
_sw.WriteLine(line);
|
||
}
|
||
}
|
||
_sw.Close();
|
||
}
|
||
|
||
public static void Test()
|
||
{
|
||
MSG_Register.ResLoginGameSuccess info = new MSG_Register.ResLoginGameSuccess();
|
||
info.infoList.Add(new MSG_Register.RoleBaseInfo());
|
||
var value = ToJson(info);
|
||
UnityEngine.Debug.Log(value);
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// List转成json
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="jsonName"></param>
|
||
/// <param name="list"></param>
|
||
/// <returns></returns>
|
||
public static string ListToJson<T>(IList<T> list, string jsonName)
|
||
{
|
||
StringBuilder Json = new StringBuilder();
|
||
if (string.IsNullOrEmpty(jsonName))
|
||
jsonName = list[0].GetType().Name;
|
||
Json.Append("{\"" + jsonName + "\":[");
|
||
if (list.Count > 0)
|
||
{
|
||
for (int i = 0; i < list.Count; i++)
|
||
{
|
||
T obj = Activator.CreateInstance<T>();
|
||
System.Reflection.PropertyInfo[] pi = obj.GetType().GetProperties();
|
||
Json.Append("{");
|
||
for (int j = 0; j < pi.Length; j++)
|
||
{
|
||
Type type = pi[j].GetValue(list[i], null).GetType();
|
||
Json.Append("\"" + pi[j].Name.ToString() + "\":" + StringFormat(pi[j].GetValue(list[i], null).ToString(), type));
|
||
|
||
if (j < pi.Length - 1)
|
||
{
|
||
Json.Append(",");
|
||
}
|
||
}
|
||
Json.Append("}");
|
||
if (i < list.Count - 1)
|
||
{
|
||
Json.Append(",");
|
||
}
|
||
}
|
||
}
|
||
Json.Append("]}");
|
||
return Json.ToString();
|
||
}
|
||
|
||
/// <summary>
|
||
/// List转成json
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="list"></param>
|
||
/// <returns></returns>
|
||
public static string ListToJson<T>(IList<T> list)
|
||
{
|
||
object obj = list[0];
|
||
return ListToJson<T>(list, obj.GetType().Name);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 对象转换为Json字符串
|
||
/// </summary>
|
||
/// <param name="jsonObject">对象</param>
|
||
/// <returns>Json字符串</returns>
|
||
public static string ToJson(object jsonObject)
|
||
{
|
||
string jsonString = "{";
|
||
System.Reflection.PropertyInfo[] propertyInfo = jsonObject.GetType().GetProperties();
|
||
var method = jsonObject.GetType().GetMethod("GetMsgID");
|
||
if (method != null)
|
||
{
|
||
var className = jsonObject.GetType().Name;
|
||
jsonString += "\"Class\":" + "'" + className + "'" + ",";
|
||
|
||
var msgID = (int)method.Invoke(jsonObject, null);
|
||
if (msgID != 0)
|
||
{
|
||
jsonString += "\"MsgID\":" + msgID + ",";
|
||
}
|
||
}
|
||
for (int i = 0; i < propertyInfo.Length; i++)
|
||
{
|
||
object objectValue = null;
|
||
try
|
||
{
|
||
objectValue = propertyInfo[i].GetGetMethod().Invoke(jsonObject, null);
|
||
}
|
||
catch (System.Exception ex)
|
||
{
|
||
UnityEngine.Debug.LogException(ex);
|
||
try
|
||
{
|
||
objectValue = propertyInfo[i].GetValue(jsonObject, null);
|
||
}
|
||
catch (System.Exception e2)
|
||
{
|
||
UnityEngine.Debug.LogException(e2);
|
||
objectValue = null;
|
||
}
|
||
}
|
||
string value = string.Empty;
|
||
|
||
if (objectValue == null)
|
||
{
|
||
value = "'null'";
|
||
}
|
||
else if (objectValue is string)
|
||
{
|
||
value = "'" + (objectValue.ToString()) + "'";
|
||
}
|
||
else if (objectValue.GetType().IsValueType)
|
||
{
|
||
value = (objectValue.ToString());
|
||
}
|
||
else if (objectValue is System.Collections.IEnumerable)
|
||
{
|
||
value = ToJson((System.Collections.IEnumerable)objectValue);
|
||
}
|
||
else
|
||
{
|
||
value = ToJson(objectValue);
|
||
}
|
||
jsonString += "\"" + (propertyInfo[i].Name) + "\":" + value + ",";
|
||
}
|
||
jsonString.Remove(jsonString.Length - 1, 1);
|
||
jsonString = jsonString.TrimEnd(',');
|
||
return jsonString + "}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 对象集合转换Json
|
||
/// </summary>
|
||
/// <param name="array">集合对象</param>
|
||
/// <returns>Json字符串</returns>
|
||
public static string ToJson(System.Collections.IEnumerable array)
|
||
{
|
||
string jsonString = "[";
|
||
foreach (object item in array)
|
||
{
|
||
jsonString += ToJson(item) + ",";
|
||
}
|
||
if (jsonString.Length == 0)
|
||
{
|
||
return "";
|
||
}
|
||
jsonString.Remove(jsonString.Length - 1, 1);
|
||
|
||
return jsonString + "]";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 普通集合转换Json
|
||
/// </summary>
|
||
/// <param name="array">集合对象</param>
|
||
/// <returns>Json字符串</returns>
|
||
public static string ToArrayString(System.Collections.IEnumerable array)
|
||
{
|
||
string jsonString = "[";
|
||
foreach (object item in array)
|
||
{
|
||
jsonString = ToJson(item.ToString()) + ",";
|
||
}
|
||
jsonString.Remove(jsonString.Length - 1, jsonString.Length);
|
||
return jsonString + "]";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 过滤特殊字符
|
||
/// </summary>
|
||
/// <param name="s"></param>
|
||
/// <returns></returns>
|
||
private static string String2Json(String s)
|
||
{
|
||
StringBuilder sb = new StringBuilder();
|
||
for (int i = 0; i < s.Length; i++)
|
||
{
|
||
char c = s.ToCharArray()[i];
|
||
switch (c)
|
||
{
|
||
case '\"':
|
||
sb.Append("\\\""); break;
|
||
case '\\':
|
||
sb.Append("\\\\"); break;
|
||
case '/':
|
||
sb.Append("\\/"); break;
|
||
case '\b':
|
||
sb.Append("\\b"); break;
|
||
case '\f':
|
||
sb.Append("\\f"); break;
|
||
case '\n':
|
||
sb.Append("\\n"); break;
|
||
case '\r':
|
||
sb.Append("\\r"); break;
|
||
case '\t':
|
||
sb.Append("\\t"); break;
|
||
default:
|
||
sb.Append(c); break;
|
||
}
|
||
}
|
||
return sb.ToString();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 格式化字符型、日期型、布尔型
|
||
/// </summary>
|
||
/// <param name="str"></param>
|
||
/// <param name="type"></param>
|
||
/// <returns></returns>
|
||
private static string StringFormat(string str, Type type)
|
||
{
|
||
if (type == typeof(string))
|
||
{
|
||
str = String2Json(str);
|
||
str = "\"" + str + "\"";
|
||
}
|
||
else if (type == typeof(DateTime))
|
||
{
|
||
str = "\"" + str + "\"";
|
||
}
|
||
else if (type == typeof(bool))
|
||
{
|
||
str = str.ToLower();
|
||
}
|
||
return str;
|
||
}
|
||
}
|
||
#endregion
|
||
}
|
||
}
|