Unity3d socket 通讯客户端

最近封装的一个socket客户端,目前调试通过了,等游戏做好了在测试稳定性

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Code.Common;
using Code.Component;
using Newtonsoft.Json.Linq;
using protobuf;
using ProtoBuf;
using UnityEngine;
using UnityEngine.Networking;

namespace Code.Core
{
    public enum NetType
    {
        None,
        Lobby,
        Game,
    }


    public enum EvtType
    {
        Connect,
        Close,
        OnMessage,
        SendMessage,
    }


    public class NetPacket
    {
        public MsgID MsgId;
        public byte[] Buf;
        public NetType NetType;
        public IExtensible Msg;
        public int BodyLength;
        public EvtType EvtType;
        public int Ret;

        public T Decode<T>()
        {
            using (var mem = new MemoryStream())
            {
                mem.SetLength(Buf.Length);
                mem.Write(Buf, 0, Buf.Length);
                mem.Position = 0;
                return Serializer.Deserialize<T>(mem);
            }
        }
    }

    public class NetClient : MonoBehaviour
    {
        private Socket _clientLobby;
        private Socket _clientGame;

        /// <summary>
        /// 发送ping时间间隔
        /// </summary>
        public int pingDiff;

        /// <summary>
        /// 网络延迟
        /// </summary>
        public float lobbyDelay;

        public float gameDelay;

        public BaseScene listener;
        private static NetClient Ins { get; set; }
        private static readonly ConcurrentQueue<NetPacket> SendQue = new ConcurrentQueue<NetPacket>();
        private static readonly ConcurrentQueue<NetPacket> RecvQue = new ConcurrentQueue<NetPacket>();

        private static long TimeOffset => (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000;
        private const int HeadLength = 4;

        [Header("测试账号")] public int TestUid;

        public string TestToken;

        public string TestIP;

        public bool Testing;

        public static void SetTestInfo()
        {
#if TEST_NEIWANG
            Const.Host = Ins.TestIP;
            Const.SetTokenAndUid(Ins.TestToken, Ins.TestUid);
#endif
        }


        private class Conn
        {
            private Socket _socket;

            private NetType NetType { get; }

            private Thread _tr;
            private volatile bool _connected;
            private volatile bool _connecting;

            public Conn(NetType netType)
            {
                NetType = netType;
                _socket = null;
                _tr = null;
            }

            public bool _Send(NetPacket packet)
            {
                if (!_connected)
                {
                    XLog.Log("connection is closed");
                    return false;
                }

                try
                {
                    var head = new byte[HeadLength];
                    var body = new byte[] { };
                    if (packet.Msg != null)
                    {
                        using (var mem = new MemoryStream())
                        {
                            Serializer.Serialize(mem, packet.Msg);
                            body = mem.ToArray();
                        }
                    }

                    long packetLength = body.Length;
                    //1.写入消息长度2字节//
                    head[0] = (byte) (packetLength & 0xFF);
                    head[1] = (byte) ((packetLength >> 8) & 0xFF);
                    //2.写入消息类型2字节//
                    head[2] = (byte) (((int) packet.MsgId) & 0xFF);
                    head[3] = (byte) (((int) packet.MsgId >> 8) & 0xFF);
                    var buf = new byte[HeadLength + packetLength];
                    Buffer.BlockCopy(head, 0, buf, 0, HeadLength);
                    if (packetLength != 0)
                    {
                        Buffer.BlockCopy(body, 0, buf, HeadLength, body.Length);
                    }

                    _socket.Send(buf);
                    return true;
                }
                catch (Exception e)
                {
                    XLog.LogError(e);
                    _ShutDown();
                }

                return false;
            }


            private void _ShutDown()
            {
                try
                {
                    _connected = false;
                    _connecting = false;
                    _socket?.Shutdown(SocketShutdown.Both);
                }
                catch (Exception e2)
                {
                    XLog.LogError(e2);
                }
            }

            public void Connect()
            {
                if (_connecting || _connected) return;
                _connecting = true;
                XLog.Log($"开始连接服务器---------------------->Host:{Const.Host} NetType:{NetType}");
                _tr = new Thread(Recv) {IsBackground = true};
                _tr.Start();
            }

            public bool IsConnected => _connected;
            public bool IsConnecting => _connecting;

            private void Recv()
            {
                try
                {
                    var head = new byte[4];
                    //创建套接字
                    var port = NetType == NetType.Lobby ? Const.LobbyPort : Const.GamePort;
                    _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    //端口及IP
                    _socket.Connect(Const.Host, port);
                    XLog.Log("连接服务器成功====================>addr:" + Const.Host + ":" + port);
                    _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
                    _socket.NoDelay = true;
                    RecvQue.Enqueue(new NetPacket {EvtType = EvtType.Connect, NetType = NetType, Ret = 1});
                    _connected = true;
                    _connecting = false;
                    while (_connected)
                    {
                        if (null == _socket)
                        {
                            Thread.Sleep(200);
                            continue;
                        }

                        _socket.Receive(head);
                        var packet = new NetPacket
                        {
                            BodyLength = (head[0] & 0x000000ff) | (head[1] & 0x000000ff) << 8,
                            MsgId = (MsgID) ((head[3] & 0x000000ff) << 8 | (head[2] & 0x000000ff)),
                            NetType = NetType,
                            EvtType = EvtType.OnMessage
                        };
                        XLog.Log($"on mesage {packet.MsgId} nettype--->{packet.NetType}");
                        if (packet.BodyLength < 8192 * 5 && packet.BodyLength > 0)
                        {
                            packet.Buf = new byte[packet.BodyLength];
                            _socket.Receive(packet.Buf);
                        }

                        RecvQue.Enqueue(packet);
                    }
                }
                catch (Exception e)
                {
                    XLog.LogError(e);
                }
                finally
                {
                    if (_connected)
                    {
                        _ShutDown();
                        RecvQue.Enqueue(new NetPacket {EvtType = EvtType.Close, NetType = NetType});
                    }
                    else
                    {
                        RecvQue.Enqueue(new NetPacket {EvtType = EvtType.Connect, NetType = NetType, Ret = 0});
                    }
                }
            }
        }


        private static readonly Conn LobbyConn = new Conn(NetType.Lobby);
        private static readonly Conn GameConn = new Conn(NetType.Game);

        public static void Connect(NetType netType)
        {
            Ins.StartCoroutine(Ins.ConnectAsync(netType));
        }

        private IEnumerator ConnectAsync(NetType netType)
        {
            LoadingObj.Show();
            var connect = false;
            if (netType == NetType.Lobby)
            {
                LobbyConn.Connect();
                while (LobbyConn.IsConnecting) yield return new WaitForSeconds(0.3f);
                connect = LobbyConn.IsConnected;
            }
            else
            {
                GameConn.Connect();
                while (GameConn.IsConnecting) yield return new WaitForSeconds(0.3f);
                connect = GameConn.IsConnected;
            }

            LoadingObj.Hide();
            if (!connect)
            {
                AlertObj.ShowNetCut();
            }
        }


        protected void Awake()
        {
            if (pingDiff < 5) pingDiff = 5;

            if (Ins == null)
            {
                Ins = this;

                DontDestroyOnLoad(gameObject);
                ThreadPool.QueueUserWorkItem(SendThread, 4);
                ThreadPool.QueueUserWorkItem(PingThread, 5);
            }
        }

        private void OnDestroy()
        {
            XLog.Log("net client dispose");
            DeAttach();
            Ins = null;
        }

        public static void Attach(BaseScene listener)
        {
            Ins.listener = listener;
            Ins.StartCoroutine(HandlePacket());
        }

        private static IEnumerator HandlePacket()
        {
            while (Ins.listener != null)
            {
                if (RecvQue.IsEmpty) yield return new WaitForEndOfFrame();
                if (RecvQue.TryDequeue(out var r))
                {
                    BaseScene listener = null;
                    while (listener is null)
                    {
                        listener = Ins.listener;
                        yield return new WaitForEndOfFrame();
                    }

                    try
                    {
                        if (r.EvtType == EvtType.Connect)
                        {
                            listener.OnConnect(r.NetType, r.Ret == 1);
                        }

                        if (r.EvtType == EvtType.Close)
                        {
                            listener.OnClose(r.NetType);
                        }
                        else if (r.EvtType == EvtType.OnMessage)
                        {
                            if (r.NetType == NetType.Lobby)
                            {
                                listener.OnLobbyMessage(r);
                            }
                            else if (r.NetType == NetType.Game)
                            {
                                listener.OnGameMessage(r);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        XLog.LogError(e);
                    }
                }

                if (Ins.listener == null) yield break;
                yield return new WaitForEndOfFrame();
            }
        }


        public static void DeAttach()
        {
            if (Ins == null) return;
            Ins.StopCoroutine(HandlePacket());
            Ins.listener = null;
        }

        #region 线程

        private void PingThread(object obj)
        {
            var packet = new NetPacket
            {
                MsgId = MsgID.MSGID_PING,
                EvtType = EvtType.SendMessage
            };
            while (Ins != null)
            {
                try
                {
                    var sec = TimeOffset;
                    if (LobbyConn.IsConnected)
                    {
                        LobbyConn._Send(packet);
                    }

                    if (GameConn.IsConnected)
                    {
                        GameConn._Send(packet);
                    }
                }
                catch (Exception e)
                {
                    XLog.LogError(e);
                }

                Thread.Sleep(pingDiff * 1000);
            }
        }


        private static void SendThread(object o)
        {
            while (Ins != null)
            {
                try
                {
                    if (SendQue.IsEmpty)
                    {
                        Thread.Sleep(200);
                        continue;
                    }

                    if (SendQue.TryDequeue(out var packet))
                    {
                        if (packet.NetType == NetType.Lobby)
                        {
                            while (!LobbyConn.IsConnected)
                            {
                                Thread.Sleep(200);
                            }

                            LobbyConn._Send(packet);
                        }
                        else if (packet.NetType == NetType.Game)
                        {
                            while (!GameConn.IsConnected)
                            {
                                Thread.Sleep(200);
                            }

                            GameConn._Send(packet);
                        }
                    }
                }
                catch (Exception e)
                {
                    XLog.LogError(e);
                }
            }
        }

        #endregion

        #region send

        public static void SendLobby(MsgID msgId, IExtensible msg)
        {
            SendQue.Enqueue(new NetPacket
            {
                Msg = msg,
                MsgId = msgId,
                NetType = NetType.Lobby,
                EvtType = EvtType.SendMessage
            });
        }

        public static void SendGame(MsgID msgId, IExtensible msg)
        {
            SendQue.Enqueue(new NetPacket
            {
                Msg = msg,
                MsgId = msgId,
                NetType = NetType.Game,
                EvtType = EvtType.SendMessage
            });
        }

        #endregion


        #region httpsupport

        public struct ReqWork
        {
            public string url;
            public IDictionary<string, object> dic;
            public Action<JObject, bool> callback;
        }

        public static void DoReqWork(IEnumerable<ReqWork> handles)
        {
            Ins.StartCoroutine(_DoReqWork(handles));
        }

        public static IEnumerator _DoReqWork(IEnumerable<ReqWork> handles)
        {
            LoadingObj.Show();
            foreach (var h in handles)
            {
                var dic = h.dic != null
                    ? new Dictionary<string, object>(h.dic)
                    : new Dictionary<string, object>();

                XLog.Log($"PostInGame      {Const.LoginType}");
                dic.Add("TOKEN", Const.Token);
                dic.Add("USRID", Const.Uid.ToString());
                dic.Add("LAN", Const.Lan);
                var json = new JObject();
                foreach (var kv in dic)
                {
                    json.Add(new JProperty(kv.Key, kv.Value));
                }

                yield return Post(h.url, json.ToString(), h.callback);
            }

            LoadingObj.Hide();
        }

        public static void Req(string url, IDictionary<string, object> dic,
            Action<JObject, bool> callback)
        {
            var self = Ins.listener;
            if (dic == null)
            {
                dic = new Dictionary<string, object>();
            }

            XLog.Log($"PostInGame      {Const.LoginType}");
            dic.Add("TOKEN", Const.Token);
            dic.Add("UID", Const.Uid);
            dic.Add("LAN", Const.Lan);
            var json = new JObject();
            foreach (var kv in dic)
            {
                json.Add(new JProperty(kv.Key, kv.Value));
            }

            self.StartCoroutine(Post(url, json.ToString(),
                (obj, success) => { callback(obj, success &&obj["success"]!=null&&obj["success"].Value<bool>()); }));
        }

        public static IEnumerator Post(string url, string postData, Action<JObject, bool> callback)
        {
            url = Const.WebHost + "/api/" + url + "/";
            XLog.Log("POST:" + url, XLog.LogColor.Blue);
            using (var www = UnityWebRequest.Post(url, postData))
            {
                yield return www.SendWebRequest();
                if (www.isHttpError || www.isNetworkError)
                {
                    callback(null, false);
                }
                else
                {
                    if (callback == null) yield break;
                    var json = JObject.Parse(www.downloadHandler.text);
                    XLog.Log(url + "--->JSON:" + json);
                    callback(json, true);
                }
            }
        }

        
        public static IEnumerator GetIp(Action<string, bool> callback)
        {
            var url = $"http://whois.pconline.com.cn/ipJson.jsp?ip={Const.Ip}&json=true";
            XLog.Log("POST GET IP:" + url, XLog.LogColor.Blue);
            using (var www = UnityWebRequest.Get(url))
            {
                yield return www.SendWebRequest();
                if (www.isHttpError || www.isNetworkError)
                {
                    callback(null, false);
                }
                else
                {
                    if (callback == null) yield break;
                    var json = JObject.Parse(www.downloadHandler.text);
                    XLog.Log(url + "--->JSON:" + json);
                    callback(json["addr"].Value<string>(), true);
                }
            }
        }
        #endregion
    }
}

 

 

发表评论

邮箱地址不会被公开。 必填项已用*标注