前面讲解了那么多,本节将是这个Socket系列的重头戏,这节会搭建一个简单的客户端通用框架,框架内容会运用到前面博客所说的内容,而且里面的内容和逻辑判断也会更加严谨,后面还会附上测试代码供大家参考~
- 核心客户端网络管理类
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using ProtoBuf;
using UnityEngine;
namespace FrameWork
{
public static class NetManager_Pro
{
public enum NetEvent_Pro
{
ConnectSuc = 1,
ConnectFail = 2,
Close = 3,
}
//套接字
private static Socket socket = null;
//接收缓冲区域
private static ByteArray readBuff = null;
//写入队列
private static Queue<ByteArray> writeQueue = null;
//是否处于关闭状态
private static bool isClosing = false;
public delegate void EventListener(String err);
private static Dictionary<NetEvent_Pro, EventListener> eventListeners =
new Dictionary<NetEvent_Pro, EventListener>();
//消息列表
private static List<IExtensible> msgList = null;
//消息列表的长度
private static int msgCount = 0;
//每一次主线程可以处理的消息数量
private static readonly int MAX_MESSAGE_COUNT = 10;
private static void InitState()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
readBuff = new ByteArray();
writeQueue = new Queue<ByteArray>();
isConnecting = false;
isClosing = false;
msgList = new List<IExtensible>();
msgCount = 0;
}
public static void AddEventListener(NetEvent_Pro netEventPro, EventListener listener)
{
if (eventListeners.ContainsKey(netEventPro))
{
eventListeners[netEventPro] += listener;
}
else
{
eventListeners[netEventPro] = listener;
}
}
public static void RemoveListener(NetEvent_Pro netEventPro, EventListener listener)
{
if (eventListeners.ContainsKey(netEventPro))
{
eventListeners[netEventPro] -= listener;
if (eventListeners[netEventPro] == null)
{
eventListeners.Remove(netEventPro);
}
}
}
private static void FireEvent(NetEvent_Pro netEventPro, String err)
{
if (eventListeners.ContainsKey(netEventPro))
{
eventListeners[netEventPro](err);
}
}
//消息事件
public delegate void MsgListener(IExtensible msg);
private static Dictionary<string, MsgListener> _msgListeners = new Dictionary<string, MsgListener>();
//消息监听
public static void AddMsgListener(string msgName, MsgListener listener)
{
if (_msgListeners.ContainsKey(msgName))
{
_msgListeners[msgName] += listener;
}
else
{
_msgListeners[msgName] = listener;
}
}
//消息移除
public static void RemoveMsgListener(string msgName, MsgListener listener)
{
if (_msgListeners.ContainsKey(msgName))
{
_msgListeners[msgName] -= listener;
if (_msgListeners[msgName] == null)
{
_msgListeners.Remove(msgName);
}
}
}
//消息分发
public static void FireMsg(string msgName, IExtensible msgBase)
{
if (_msgListeners.ContainsKey(msgName))
{
_msgListeners[msgName](msgBase);
}
}
//是否正在连接
private static bool isConnecting = false;
/// <summary>
/// 连接到服务器
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
public static void ConnectServer(string ip, int port)
{
if (socket != null && socket.Connected)
{
Debug.Log("Connct Fail,Already Connected !");
return;
}
if (isConnecting)
{
Debug.Log("Connct Fail,isConnecting......!");
return;
}
//初始化成员变量
InitState();
if (socket != null)
{
socket.NoDelay = true;
isConnecting = true;
socket.BeginConnect(ip, port, ConnectCallBack, socket);
}
}
private static void ConnectCallBack(IAsyncResult ar)
{
try
{
Socket socket = ar.AsyncState as Socket;
socket.EndConnect(ar);
Debug.Log("Socket Connect Succ ");
FireEvent(NetEvent_Pro.ConnectSuc, "");
isConnecting = false;
//异步接收数据
socket.BeginReceive(readBuff.bytes, readBuff.writeInx, readBuff.remain, 0, ReceiveCallBack, socket);
}
catch (SocketException e)
{
Debug.LogError("Socket Connect Fail " + e);
FireEvent(NetEvent_Pro.ConnectFail, "");
isConnecting = false;
}
}
//接收数据的回调
private static void ReceiveCallBack(IAsyncResult ar)
{
try
{
Socket socket = ar.AsyncState as Socket;
//获取接收的数据长度
int count = socket.EndReceive(ar);
if (count == 0)
{
Close();
return;
}
readBuff.writeInx += count;
//具体的处理服务器发过来消息的函数
OnReceiveDate();
//继续接收数据,如果数组剩余长度不够的化,那就扩容
if (readBuff.remain < 8)
{
readBuff.MoveBytes();
readBuff.ReSize(readBuff.Length * 2); //动态扩容
}
socket.BeginReceive(readBuff.bytes, readBuff.writeInx, readBuff.remain, 0, ReceiveCallBack, socket);
}
catch (SocketException e)
{
Debug.LogError("Socket Receive Fail " + e);
}
}
//处理服务器发过来的消息
private static void OnReceiveDate()
{
//消息长度太短了,不足以构成一条消息
if (readBuff.Length <= 2) return;
int readInx = readBuff.readInx;
byte[] bytes = readBuff.bytes;
//消息体长度
Int16 bodyLength = (Int16) ((bytes[readInx + 1] << 8 | bytes[readInx])); //统一以小端编码
//消息体 缓冲区长度大于2 但是还不足以组成一条消息 即 有消息,但是消息不全
if (readBuff.Length < bodyLength) return;
readBuff.readInx += 2;
//解析协议名字
int nameCount = 0;
string protoName = ProMsgHelp.DecodeName(readBuff.bytes, readBuff.readInx, out nameCount);
Debug.Log("NameCount : " + nameCount);
if (string.IsNullOrEmpty(protoName)) return;
readBuff.readInx += nameCount;
int bodyCount = bodyLength - nameCount;
IExtensible msgBase = ProMsgHelp.Decode(protoName, readBuff.bytes, readBuff.readInx, bodyCount);
readBuff.readInx += bodyCount;
readBuff.CheckAndMoveBytes();
//添加到消息队列
lock (msgList)
{
msgList.Add(msgBase);
}
++msgCount;
//还有消息,那就继续读取消息吧~~~
if (readBuff.Length > 2)
{
OnReceiveDate();
}
}
public static void OnTick()
{
OnMsgTick();
}
private static void OnMsgTick()
{
if (msgCount == 0) return;
for (int i = 0; i < MAX_MESSAGE_COUNT; i++)
{
IExtensible msgBase = null;
lock (msgList)
{
if (msgList.Count > 0)
{
msgBase = msgList[0];
msgList.RemoveAt(0);
--msgCount;
}
}
//分发消息
if (msgBase != null)
{
Debug.Log("OnMsgTick::: msgBase ===>" + msgBase.ToString());
FireMsg(msgBase.ToString(), msgBase);
}
else
{
break;
}
}
}
public static void Close()
{
if (socket == null || !socket.Connected)
{
return;
}
if (isConnecting)
{
return;
}
//还有数据要发送
if (writeQueue.Count > 0)
{
isClosing = true;
}
else
{
socket.Close();
FireEvent(NetEvent_Pro.Close, "");
}
}
/// <summary>
/// 发送数据到服务器
/// </summary>
/// <param name="msgBase"></param>
public static void Send(IExtensible msg)
{
if (socket == null || !socket.Connected)
{
return;
}
if (isConnecting)
{
return;
}
//数据进行编码
byte[] nameBytes = ProMsgHelp.EncodeName(msg);
byte[] bodyBytes = ProMsgHelp.Encode(msg);
int len = nameBytes.Length + bodyBytes.Length;
byte[] sendBytes = new byte[2 + len];
//组装长度
sendBytes[0] = (byte) (len % 256);
sendBytes[1] = (byte) (len / 256);
//组装名字
Array.Copy(nameBytes, 0, sendBytes, 2, nameBytes.Length);
Array.Copy(bodyBytes, 0, sendBytes, 2 + nameBytes.Length, bodyBytes.Length);
ByteArray byteArray = new ByteArray(sendBytes);
int count = 0;
lock (writeQueue) //由于异步的机制可能会导致回到函数执行在不同的线程之间,所以启用线程锁来防止线程之间的资源争夺问题
{
writeQueue.Enqueue(byteArray);
count = writeQueue.Count;
}
//发送数据
if (count == 1)
{
socket.BeginSend(sendBytes, 0, sendBytes.Length, 0, SendCallBack, socket);
}
}
private static void SendCallBack(IAsyncResult ar)
{
Socket socket = ar.AsyncState as Socket;
if (socket == null || !socket.Connected)
{
return;
}
int count = socket.EndSend(ar);
ByteArray ba = null;
lock (writeQueue)
{
ba = writeQueue.Peek();
}
ba.readInx += count;
//说明数据发送完整了
if (ba.Length == 0)
{
lock (writeQueue)
{
writeQueue.Dequeue();
if (writeQueue.Count > 0)
{
ba = writeQueue.Peek();
}
else
{
ba = null;
}
}
}
//说明还有数据没有发送,那就继续发送数据吧~
if (ba != null)
{
socket.BeginSend(ba.bytes, ba.readInx, ba.Length, 0, SendCallBack, socket);
}
//如果正在关闭时候
else if (isClosing)
{
//直接关闭socket
socket.Close();
}
}
}
}
- 测试类
using FrameWork.ProtoBuf;
using ProtoBuf;
using UnityEngine;
namespace FrameWork
{
public class Test1 : MonoBehaviour
{
private string ip = "127.0.0.1";
private int port = 8888;
// Start is called before the first frame update
void Start()
{
NetManager_Pro.AddEventListener(NetManager_Pro.NetEvent_Pro.ConnectSuc, OnConnectSucc);
NetManager_Pro.AddEventListener(NetManager_Pro.NetEvent_Pro.ConnectFail, OnConnectFail);
NetManager_Pro.AddEventListener(NetManager_Pro.NetEvent_Pro.Close, OnClose);
NetManager_Pro.AddMsgListener("MsgMove_Pro", OnMsgMove_Pro);
TestProtobuf();
}
private void OnMsgMove_Pro(IExtensible msg)
{
MsgMove_Pro msgMovePro = msg as MsgMove_Pro;
if (msgMovePro != null)
{
Debug.Log("OnMsgMove_Pro msgMove.x = " + msgMovePro.x);
Debug.Log("OnMsgMove_Pro msgMove.y = " + msgMovePro.y);
Debug.Log("OnMsgMove_Pro msgMove.z = " + msgMovePro.z);
}
}
/// <summary>
/// 玩家点击连接按钮
/// </summary>
public void OnConnectClick()
{
NetManager_Pro.ConnectServer(ip, port);
}
/// <summary>
/// 玩家点击断开连接按钮
/// </summary>
public void OnCloseClick()
{
NetManager_Pro.Close();
}
/// <summary>
/// 玩家点击移动按钮
/// </summary>
public void OnMoveClick()
{
MsgMove_Pro msgMovePro = new MsgMove_Pro();
msgMovePro.x = 12;
msgMovePro.y = 16;
msgMovePro.z = 18;
NetManager_Pro.Send(msgMovePro);
}
private void OnClose(string err)
{
Debug.Log("关闭连接......");
}
private void OnConnectFail(string err)
{
Debug.Log("连接好像失败咯......");
}
private void OnConnectSucc(string err)
{
Debug.Log("连接成功啦~~~");
}
private void Update()
{
NetManager_Pro.OnTick();
}
void TestProtobuf()
{
MsgMove_Pro msgMovePro = new MsgMove_Pro();
msgMovePro.x = 666;
//编码
byte[] bs = ProMsgHelp.Encode(msgMovePro);
Debug.Log(System.BitConverter.ToString(bs));
Debug.Log(msgMovePro.ToString());
byte[] test = ProMsgHelp.EncodeName(msgMovePro);
int count = 0;
string className = ProMsgHelp.DecodeName(test, 0, out count);
Debug.Log(className);
Debug.Log(count);
//解码
IExtensible m = ProMsgHelp.Decode(msgMovePro.ToString(), bs, 0, bs.Length);
MsgMove_Pro msgMovePro2 = m as MsgMove_Pro;
Debug.Log("msgMovePro2::: " + msgMovePro2.x);
}
}
}
以上就是一个简单的客户端框架吧,希望对大家有所帮助吧~~~