学习日记#07
客户端网络管理
public string SecretKey {get;set;}
public string PublicKey = "abc1234";
private List<MsgBase> m_MsgList; // 存储所有解析完成的协议
private List<MagBase> m_UnityMsgList; // 存储所有要在unity处理的消息
private int m_MsgCount; // 消息计数器
private Thread m_MsgThread; // 消息线程
private Thread m_HeartThread; // 心跳线程
private long m_PingIntervale; // 心跳间隔
public delegate void ProtoListener(MsgBase msgBase); // 根据不同的协议监听不同的函数
private Dictionary<ProtocolEnum, ProtoListener> m_ProtoDic; // 存储不同协议所对应的不同的函数
private Queue<ByteArray> m_writeQueue; // 消息写入队列
private long lastPingTime; // 发送心跳包的时间
private long lastPongTime;// 接受心跳包的时间
public void Connect(string ip, int port)
{
if(m_Socket != null && m_Socket.Connected)
{
Debug.LogError("链接失败,已经链接过了");
return;
}
InitState();
m_Socket.NoDelay = true;
m_Socket.BeginConnect(ip, port, ConnectCallback, m_Socket);// ????????????????
}
// 初始化状态
void InitState()
{
m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
byteArray = new ByteArray();
m_MsgCount = 0;
m_MsgList = new List<MsgBase>();
m_UnityMsgList = new List<MsgBase>();
m_ProtoDic = new Dictionary<ProtocolEnum, ProtoListener>();
m_writeQueue = new Queue<ByteArray>();
lastPingTime = GetTimeStamp();
lastPongTime = GetTimeStam();
}
private int GetTimeStamp()
{
TimeSpan timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(timeSpan.TotalSeconds);
}
void ConnectCallback(IAsyncResult asyncResult)// ??????????
{
Debug.Log("已连接服务器");
try
{
Socket socket = asyncResult.AsyncState as Socket;
socket.EndConnect(asyncResult);// ????????关闭避免重复请求
m_MsgThread = new Thread(MsgThread);// 线程里面是一个函数
m_MsgThread.IsBackground= true; // 在后台也 开启
m_MsgThread.Start();// ?????????线程开启
m_HeartThread = new Thread(PingThread);
ProtoManager.CSSecret();// 协议管理器同一发送消息
// 接受服务器的消息
m_Socket.BeginReceive(bytrArray.Bytes, byteArray.WriteIdx, byteArray.Remain, 0, ReceiveCallback, socket);// ?????????/
}
catch(Exception)
}
try
{
Socket socket = asyncResult as Socket;
int count = socket.EndReceive(asyncResult);
if (count <= 0)
{
Close();
return;
}
byteArray.WriteIdx += count;
// 对接收到的消息进行处理
OnReceiverData();
if (byteArray.Remian < 8)
{
byteArray.CheckAndMoveBytes();
byteArray.Resize(byteArray.Length * 2);
}
socket.BeginReceive(byteArray.Bytes, byteArray.WriteIdx, byteArray.Remian, 0,/*接受数据回调*/ReceiveCallback, socket
);
}
catch (SocketException e)
{
Debug.LogError("接受数据回调异常" + e.ToString());
Close();
}
}
void OnReceiverData()
{
if (byteArray.Length < 4 || byteArray.ReadIdx < 0)
{
Close();
}
int readIdx = byteArray.ReadIdx;
byte[] bytes = byteArray.Bytes;
int bodyLength = BitConverter.ToInt32(bytes, readIdx);
if (bodyLength + 4 > byteArray.Length)// 完整性验证 分包处理
{
return;
}
// 对完整数据进行解析
byteArray.ReadIdx += 4;
// 解析协议名
int nameCount = 0;
ProtocolEnum protocol = ProtocolEnum.None;
try
{
protocol = MsgBase.DecodeName(byteArray.Bytes, byteArray.ReadIdx, out nameCount);
}
catch(Exception e)
{
Debug.Log(e.ToString());
Close();
return;
}
// 协议正确性判断
if (protocol == ProtocolEnum.None)
{
Debug.Log("协议名错误");
Close();
return;
}
// 解析协议内容
byteArray.ReadIdx += nameCount;
int bodyCount = bodyLength - nameCount;
MsgBase msgBase = null;
try
{
msgBase = MsgBase.Decode(protocol, byteArray.Bytes, byteArray.ReadIdx, bodyCount);
}
catch(Exception e)
{
Debug.LogError(e.ToString());
Close();
return;
}
if (msgBase == null)
{
Debug.Log("协议内容错误");
return;
}
byteArray.ReadIdx += bodyCount;
byteArray.CheckAndMoveBytes();
// 上述 内容解析完成
// 反射执行消息处理办法
// 不要反射
// 消息类型 业务类型(Unity处理) 工具类型
//MethodInfo methodInfo = typeof(MsgHandler).GetMethod(protocol.ToString());
//object[] obj = { msgBase };
//if (methodInfo != null)
//{
// methodInfo.Invoke(null, obj);
//}
//else
//{
// Debug.Log("消息处理办法不存在");
//}
// 将解析完成的协议进行处理
lock (m_MsgList)
{
m_MsgList.Add(msgBase);
m_MsgCount++;
}
if (byteArray.Length > 4) // 粘包
{
OnReceiverData();
}
}
void MsgThread()
{
while(m_Socket != null && m_Socket.Connected)
{
Debug.Log("消息线程执行");
if (m_MsgList.Count <= 0)
{
continue;
}
else
{
MsgBase msgBase = null;
lock (m_MsgList)// 多线程中,写入原子消息
{
if (m_MsgList.Count > 0)
{
msgBase = m_MsgList[0];
m_MsgList.RemoveAt(0);
}
}
if (msgBase != null)
{
if (msgBase is MsgPing)
{
lastPongTime = GetTimeStamp();
Debug.Log("客户端接受心跳包");
m_MsgCount--;
}
else
{
lock(m_UnityMsgList)
{
m_UnityMsgList.Add(msgBase);
}
}
}
else
{
break;
}
}
}
}
private void PingThread()
{
while(m_Socket != null && m_Socket.Connected)
{
long timeNow = GetTimeStamp();
if (timeNow - lastPingTime > m_PingInterval)
{
MsgPing msgPing = new MsgPing();
SendMessage(msgPing); // 例外的协议包发送
lastPingTime = GetTimeStamp();
}
if (timeNow - lastPongTime > m_PingInterval * 4)// 服务器长时间未响应
{
Debug.Log("服务器长时间未响应");
Close();
}
}
}
public void SendMessage(MsgBase msgBase)
{
if (m_Socket == null || !m_Socket.Connected)
{
return;
}
// 创建消息字节数组
byte[] nameBytes = MsgBase.EncodeName(msgBase);
byte[] bodyBytes = MsgBase.Encode(msgBase);
int bodyLength = nameBytes.Length + bodyBytes.Length;
byte[] headBytes = BitConverter.GetBytes(bodyLength);
// 组装发送的字节数组
byte[] sendBytes = new byte[headBytes.Length + nameBytes.Length + bodyBytes.Length];
Array.Copy(headBytes, 0, sendBytes, 0, headBytes.Length); // 头部长度
Array.Copy(nameBytes, 0, sendBytes, headBytes.Length, nameBytes.Length); // 消息名字节
Array.Copy(bodyBytes, 0, sendBytes, headBytes.Length + nameBytes.Length, bodyBytes.Length); // 消息体字节
ByteArray ba = new ByteArray(sendBytes);
int count = 0;
lock (m_writeQueue)
{
m_writeQueue.Enqueue(ba);
count = m_writeQueue.Count;
}
if(count == 1)// ?????????????????????
{
m_Socket.BeginSend(sendBytes, 0 , sendBytes, 0, SendCallback, m_Socket);
}
}
private void SendCallback(IAsyncResult asyncResult)
{
try
{
Socket socket = asyncResult.AsyncState as Socket;
if (socket == null || !socket.Connected())
{
return;
}
int count = socket.EndSend(asyncResult);
// 判断发送是否完成
ByteArray ba;
lock (m_writeQueue)
{
ba = m_writeQueue.First();
}
ba.ReadIdx += count;
if (ba.Length == 0) // 一条消息发送完整
{
lock (m_writeQueue)
{
m_writeQueue.Dequeue();
if (m_writeQueue.Count > 0)
{
ba = m_writeQueue.First();
}
else
{
ba = null;
}
}
}
if (ba != null)
{
socket.BeginSend(ba.Bytes, ba.ReadIdx, ba.Length, 0, SendCallback, m_Socket);// ??????????????????分包???????
}
}
}
public void Close()
{
if (m_Socket == null)
{
return;
}
SecretKey = "";
m_Socket.Close();
if (m_MsgThread != null && m_MsgThread.IsAlive)
{
m_MsgThread.Abort();
m_MsgThread = null;
}
if (m_PingThread != null && m_PingThread.IsAlive)
{
m_PingThread.Abort();
m_PingThread = null;
}
Debug.Log("关闭连接");
}
-------
public void MsgUpdate()
{
if (m_Socket != null && m_Socket.Connected)
{
if (m_MsgCount == 0)
{
return ;
}
else
{
MsgBase magBase = null;
lock (m_UnityMsgList)
{
if (m_UnityMsglist.Count > 0)
{
msgBase = m_UnityMsgList[0];
m_UnityMsgList.RemoveAt(0);
m_MsgCount--;
}
}
if(magBase != null)
{
StatrProto(msgBase.ProtocolType, msgBase);
}
}
}
}
// 对协议进行监听
public void AddProtoListener(ProtocolEnum protocol, ProtoListener protoListener)
{
if (!m_ProtoDic.ContainsKey(protocol))
{
m_ProtoDic[protocol] = protoListener;
}
}
// 执行协议
public void StartProto(ProtocolEnum protocol, MsgBase msgBase)
{
if (m_ProtoDic.ContainsKey(protocol))
{
m_ProtoDic[protocol](msgBase);
}
}
client -> socket -> connect -> ConenctCall(receive)->ReceiveCall(ReceiveCall)
当前客户端线程形成接受消息循环,
创建消息线程处理消息列表,工具逻辑自身处理,业务逻辑加入unity消息队列
创建心跳线程处理心跳包持续发送和活动客户端检查
注意多线程下的线程安全问题
msgUpdate开放给外部处理 Unity消息