游戏开发之网络篇_09 Socket通信客户端框架搭建和使用

26 篇文章 1 订阅
10 篇文章 0 订阅

前面讲解了那么多,本节将是这个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);
        }
    }
}

以上就是一个简单的客户端框架吧,希望对大家有所帮助吧~~~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值