最近封装的一个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 } }