Unity Socket服务器(客户端)通用框架

  • 通用服务端框架,具有粘包半包处理、协议解析等功能,服务端使用多路复用,单进程单线程
  • 网络相关代码
using Assets.Scripts.Net.Proto;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using UnityEngine;
public delegate void EventListener(string err);
public delegate void MsgListener(MsgBase msg);
public static class NetManager
    //是否启用心跳
    public static bool isUsePing = true;
    //心跳间隔时间
    public static int pingInterval = 30;
    //上一次发送Ping时间
    public static float lastPingTime = 0;
    //上一次接收Pong时间
    public static float lastPongTime = 0;
    //消息监听字典
    private static Dictionary<string, MsgListener> MsgListenerDic = new Dictionary<string, MsgListener>();
    //事件监听字典
    private static Dictionary<NetEvent, EventListener> EventListenerDic = new Dictionary<NetEvent, EventListener>();
    //是否正在断开连接
    private static bool isClosing;
    //是否正在连接
    private static bool isConnecting;
    private static Socket socket;
    //读取数据缓存
    private static byte[] readBuff;
    //数据缓存大小
    public static int BuffTotalCount = 2048;
    //接收缓冲区的数据长度
    private static int buffcount;
    //发送消息缓存队列
    private static Queue<byte[]> writeQueue;
    //接收消息缓存列表
    private static List<MsgBase> msgList;
    //接收消息缓存列表数量
    private static int msgCount;
    //每一次Update处理的消息量
    readonly static int MAX_MESSAGE_FIRE = 10;
    //初始化
    private static void InitState()
        //上次发送Ping时间
        lastPingTime = Time.time;
        //上次收到Pong时间
        lastPongTime = Time.time;
        isClosing = false;
        socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
        readBuff = new byte[BuffTotalCount];
        writeQueue = new Queue<byte[]>();
        msgList = new List<MsgBase>();
        msgCount = 0;
        buffcount = 0;
        if (!MsgListenerDic.ContainsKey("MsgPong"))
            MsgListenerDic.Add("MsgPong", OnMsgPong);
    /// <summary>
    /// 发送Ping协议
    /// </summary>
    private static void PingUpdate()
        if (!isUsePing)
            return;
        if (socket==null||!socket.Connected)
            return;
        //发送Ping
        if (Time.time-lastPingTime>pingInterval)
            MsgPing ping = new MsgPing("MsgPing");
            Send(ping);
            lastPingTime = Time.time;
        if (Time.time - lastPongTime > pingInterval*4)
            socket.Close();
            socket.Dispose();
    private static void OnMsgPong(MsgBase msg)
        lastPongTime = Time.time;
    /// <summary>
    /// 连接服务器
    /// </summary>
    /// <param name="ip">服务器Ip</param>
    /// <param name="port">端口号</param>
    public static void Connect(string ip , int port)
        if (socket!=null&&socket.Connected)
            Debug.Log("已连接!!!");
            return;
        if (isConnecting)
            Debug.Log("正在连接.....");
        isConnecting = false;
        InitState();
        socket.BeginConnect(ip,port, ConnectCallBack,socket);
    /// <summary>
    /// 断开连接
    /// </summary>
    public static void Close()
        if (socket==null||!socket.Connected)
            Debug.Log("未能断开连接,原因:未连接服务器");
            return;
        if (isConnecting)
            Debug.Log("未能断开连接,原因:未连接服务器");
            return;
        if (writeQueue.Count>0)
            isClosing = true;
            socket.Close();
            FireEvent(NetEvent.Close,"");
    /// <summary>
    /// 发送消息
    /// </summary>
    public static void Send(MsgBase msg)
        if (socket==null||!socket.Connected)
            Debug.Log("发送失败,未连接服务器!");
            return;
        if (isConnecting)
            Debug.Log("发送失败,正在连接服务器。。。。");
            return;
        if (isClosing)
            Debug.Log("发送失败,正在断开连接。。。。");
            return;
        //数据编码
        byte[] nameBytes = MsgBase.EncodeProtoName(msg);
        byte[] bodyBytes = MsgBase.Encode(msg);
        int totalLen = nameBytes.Length + bodyBytes.Length;
        byte[] sendBytes = new byte[totalLen + 2];
        sendBytes[0] = (byte)(totalLen % 256);
        sendBytes[1] = (byte)(totalLen / 256);
        Array.Copy(nameBytes, 0, sendBytes, 2, nameBytes.Length);
        Array.Copy(bodyBytes,0,sendBytes, nameBytes.Length+2,bodyBytes.Length);
        int count = 0;
        lock (writeQueue)
            writeQueue.Enqueue(sendBytes);
            count = writeQueue.Count;
        if (count>0)
            socket.BeginSend(sendBytes,0,sendBytes.Length,0, SendCallBack,socket);
    /// <summary>
    /// 更新消息
    /// </summary>
    public static void MsgUpdate()
        if (msgCount == 0)
            return;
        for (int i = 0; i < MAX_MESSAGE_FIRE; i++)
            //获取第一条消息
            MsgBase msg = null;
            lock (msgList)
                if (msgList.Count>0)
                    msg = msgList[0];
                    msgList.RemoveAt(0);
                    msgCount--;
            if (msg!=null)
                FireMsg(msg.protoName,msg);
                break;
    /// <summary>
    /// 发送消息回调
    /// </summary>
    /// <param name="asyncResult"></param>
    private static void SendCallBack(IAsyncResult asyncResult)
        Socket socket = asyncResult.AsyncState as Socket;
        if (socket==null||!socket.Connected)
            return;
        //EndSend
        int count = socket.EndSend(asyncResult);
        //取出队列第一个消息
        byte[] ba;
        lock (writeQueue)
            ba = writeQueue.Peek();
        int idx = count;
        if (ba.Length == count)
            idx = 0;
            lock (writeQueue)
                writeQueue.Dequeue();
                ba = writeQueue.Count > 0 ? writeQueue.Peek() : null;
        if (ba != null)
            socket.BeginSend(ba,idx,ba.Length,0,SendCallBack,socket);
        else if(isClosing)
            socket.Close();
    //连接回调
    private static void ConnectCallBack(IAsyncResult asyncResult)
            Socket socket = asyncResult.AsyncState as Socket;
            socket.EndConnect(asyncResult);
            isConnecting = false;
            FireEvent(NetEvent.ConnectSucc,"");
            socket.BeginReceive(readBuff,buffcount,BuffTotalCount-buffcount,0, ReciveCallBack,socket);
        }catch(SocketException ex)
            isConnecting = false;
            Debug.Log("连接失败:"+ex.ToString());
            FireEvent(NetEvent.ConnectFail,"");
    private static void ReciveCallBack(IAsyncResult asyncResult)
        Socket socket = asyncResult.AsyncState as Socket;
        int count = socket.EndReceive(asyncResult);
        buffcount += count;
        OnReciveData();
        //继续接收数据
        socket.BeginReceive(readBuff,buffcount,BuffTotalCount- buffcount, 0,ReciveCallBack,socket);
    /// <summary>
    /// 处理缓冲区消息(解决粘包问题)
    /// </summary>
    private static void OnReciveData()
        //消息不足够长
        if (buffcount <= 2)
            return;
        Int16 bodyLen = (Int16)(readBuff[1] << 8 | readBuff[0]);
        if (buffcount<bodyLen+2)
            return;
        //string s = Encoding.UTF8.GetString(readBuff,2,bodyLen);
        int nameCount = 0;
        int start = 2 + bodyLen;
        int count = buffcount - start;
        //解析协议名
        string protoName = MsgBase.DecodeProtoName(readBuff,2,out nameCount);
        if (protoName=="")
            Debug.Log("OnReciveData MsgBase.DecodeProtoName:Fail");
            return;
        int readIdx = 2 + nameCount;
        MsgBase msgBase = MsgBase.Decode(protoName,readBuff,readIdx,bodyLen-nameCount);
        //添加消息
        lock (msgList)
            msgList.Add(msgBase);
        msgCount++;
        Array.Copy(readBuff,start,readBuff,0,count);
        buffcount -= start;
        OnReciveData();
    //分发消息
    private static void FireEvent(NetEvent netEvent,string err)
        if (EventListenerDic.ContainsKey(netEvent)&& EventListenerDic[netEvent]!=null)
            EventListenerDic[netEvent](err);
    /// <summary>
    /// 添加监听事件
    /// </summary>
    /// <param name="netEvent"></param>
    /// <param name="listener"></param>
    public static void AddListener(NetEvent netEvent,EventListener listener)
        if (EventListenerDic.ContainsKey(netEvent))
            EventListenerDic[netEvent] += listener;
            EventListenerDic.Add(netEvent,listener);
    /// <summary>
    /// 移除监听事件
    /// </summary>
    /// <param name="netEvent"></param>
    /// <param name="listener"></param>
    public static void RemoveListener(NetEvent netEvent, EventListener listener)
        if (EventListenerDic.ContainsKey(netEvent))
            if (EventListenerDic[netEvent]!=null)
                EventListenerDic[netEvent] -= listener;
                EventListenerDic.Remove(netEvent);
    /// <summary>
    /// 添加消息监听
    /// </summary>
    /// <param name="msgName">消息名称</param>
    /// <param name="msgListener">回调函数</param>
    public static void AddMsgListener(string msgName , MsgListener msgListener)
        if (MsgListenerDic.ContainsKey(msgName))
            MsgListenerDic[msgName] += msgListener;
            MsgListenerDic[msgName] = msgListener;
    /// <summary>
    /// 移除消息监听
    /// </summary>
    /// <param name="msgName">消息类型名称</param>
    /// <param name="msgListener">回调函数</param>
    public static void RemoveMsgListener(string msgName,MsgListener msgListener)
        if (MsgListenerDic.ContainsKey(msgName))
            if (MsgListenerDic[msgName]!=null)
                MsgListenerDic[msgName] -= msgListener;
                MsgListenerDic.Remove(msgName);
    /// <summary>
    /// 分发消息
    /// </summary>
    /// <param name="msgName">消息类型</param>
    /// <param name="msg">消息类</param>
    private static void FireMsg(string msgName,MsgBase msg)
        if (MsgListenerDic.ContainsKey(msgName)&& MsgListenerDic[msgName]!=null)
            MsgListenerDic[msgName](msg);
    public static void Update()
        MsgUpdate();
        PingUpdate();
public enum NetEvent
    ConnectSucc = 1,
    ConnectFail = 2,
    Close = 3,

客户端MsgBase 协议基类

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using Assets.Scripts.Models;
[System.Serializable]
public class MsgBase
    //协议名
    public string protoName="";
    public MsgBase(string protoName)
        this.protoName = protoName;
    public static byte[] Encode(MsgBase msgBase)
        string json = JsonUtility.ToJson(msgBase);
        return Encoding.UTF8.GetBytes(json);
    public static MsgBase Decode(string protoName,byte[] bytes,int offset,int count)
        string json = Encoding.UTF8.GetString(bytes,offset,count);
        string typeName = "Assets.Scripts.Net.Proto." + protoName;
        MsgBase msg = (MsgBase)JsonUtility.FromJson(json,Type.GetType(typeName));
        return msg;
    /// <summary>
    /// 编码协议名
    /// </summary>
    /// <param name="msg">协议</param>
    /// <returns></returns>
    public static byte[] EncodeProtoName(MsgBase msg)
        byte[] nameBytes = Encoding.UTF8.GetBytes(msg.protoName);
        int nameLen = nameBytes.Length;
        byte[] ret = new byte[nameLen+2];
        ret[0] = (byte)(nameLen % 256);
        ret[1] = (byte)(nameLen / 256);
        Array.Copy(nameBytes,0,ret,2,nameLen);
        return ret;
    /// <summary>
    /// 解码协议名
    /// </summary>
    /// <param name="bytes"></param>
    /// <param name="offset"></param>
    /// <param name="count"></param>
    /// <returns></returns>
    public static string DecodeProtoName(byte[] bytes,int offset,out int count)
        count = 0;
        if (offset+2>bytes.Length)
            return "";
        //读取长度
        Int16 len = (Int16)(bytes[offset + 1] << 8 | bytes[offset]);
        if (len<=0)
            count = 2;
            return "";
        if (offset+2+len>bytes.Length)
            return "";
        count = 2 + len;
        string name = Encoding.UTF8.GetString(bytes,offset+2,len);
        return name;

服务端NetManager

static Socket listenfd; //客户端Socket和状态信息 public static Dictionary<Socket, ClientState> clients = new Dictionary<Socket, ClientState>(); //Select的检查列表 public static List<Socket> checkRead = new List<Socket>(); //客户端ping间隔时间 public static int pingInterval; public static bool isUsePing = true; /// <summary> /// 启动服务器 /// </summary> /// <param name="port"></param> public static void StartLoop(int port,bool isPing=true,int pingTime = 30) pingInterval = 30; isUsePing = isPing; pingInterval = pingTime; listenfd = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); IPAddress ip = IPAddress.Parse("127.0.0.1"); IPEndPoint endPoint = new IPEndPoint(ip,port); listenfd.Bind(endPoint); listenfd.Listen(0); Console.WriteLine("服务器启动成功:"+DateTime.Now.ToString()); while (true) ResetCheckRead(); Socket.Select(checkRead,null,null,1000); for (int i = checkRead.Count-1; i >=0; i--) Socket item = checkRead[i]; if (item==listenfd) //服务端收到请求连接 ReadListenfd(listenfd); //客户端发来消息 ReadClientfd(item); OnTimer(); public static void ResetCheckRead() checkRead.Clear(); checkRead.Add(listenfd); foreach (ClientState item in clients.Values) checkRead.Add(item.socket); //读取Listenfd public static void ReadListenfd(Socket listenfd) Socket clientfd = listenfd.Accept(); Console.WriteLine("Accept :"+clientfd.RemoteEndPoint.ToString()); ClientState state = new ClientState(); state.socket = clientfd; state.lastPingTime = GetTimeSpan(); clients.Add(clientfd,state); catch (Exception ex) Console.WriteLine("Accept Fail:" + ex.ToString()); //读取Clientfd public static void ReadClientfd(Socket clientfd) ClientState state = clients[clientfd]; int count = clientfd.Receive(state.readBuff,state.buffCount,state.capacity-state.buffCount,0); state.buffCount += count; if (count<=0) //TODO:关闭连接 分发事件 Close(state); catch (SocketException ex) Console.WriteLine("Recive Exception:"+ex.ToString()); Close(state); OnReciveData(state); /// <summary> /// 解析协议(处理粘包半包 长度信息法) /// </summary> /// <param name="state"></param> private static void OnReciveData(ClientState state) Console.WriteLine("Recv:"+Encoding.UTF8.GetString(state.readBuff)); byte[] readBuff = state.readBuff; if (state.buffCount<=2) return; Int16 bodyLen = Convert.ToInt16(readBuff[1] << 8 | readBuff[0]); if (state.buffCount<bodyLen+2) return; int protoNameLen = 0; string protoName = MsgBase.DecodeProtoName(readBuff,2,out protoNameLen); MsgBase msg = MsgBase.Decode(protoName,readBuff,2+protoNameLen, bodyLen- protoNameLen); //处理消息 MethodInfo method = typeof(MsgHandler).GetMethod("On"+protoName); if (method!=null) object[] param = { state, msg }; method.Invoke(null,param); Console.WriteLine("OnReciveData Invoke Fail:"+protoName); int start = 2+bodyLen; int count = state.buffCount - start; Array.Copy(state.readBuff,start,state.readBuff,0,count); state.buffCount -= start; OnReciveData(state); /// <summary> /// 关闭连接 /// </summary> public static void Close(ClientState state) MethodInfo method = typeof(EventHandler).GetMethod("OnDisconnect"); if (method==null) Console.WriteLine("Close EventHandler Fun null:OnDisconnect"); object[] prarm = { state }; method.Invoke(null, prarm); state.socket.Close(); clients.Remove(state.socket); state.socket.Dispose(); /// <summary> /// 发送消息 /// </summary> /// <param name="state"></param> /// <param name="msg"></param> public static void SendMsg(ClientState state , MsgBase msg) if (state.socket==null||!state.socket.Connected) return; byte[] protoBytes = MsgBase.EncodeProtoName(msg.protoName); byte[] bodyBytes = MsgBase.Encode(msg); byte[] sendData = new byte[protoBytes.Length+ bodyBytes.Length+2]; int totalLen = protoBytes.Length + bodyBytes.Length; sendData[0] = Convert.ToByte(totalLen % 256); sendData[1] = Convert.ToByte(totalLen / 256); Array.Copy(protoBytes,0,sendData,2,protoBytes.Length); Array.Copy(bodyBytes, 0, sendData, protoBytes.Length+2, bodyBytes.Length); lock (state.sendQueue) state.sendQueue.Enqueue(sendData); byte[] data = state.sendQueue.Peek(); state.socket.BeginSend(data, 0, data.Length, 0, OnSendCallBack, state.socket); }catch(SocketException ex) Console.WriteLine("SendMsg Fail:"+ex.ToString()); /// <summary> /// 发送消息回调 /// </summary> /// <param name="asyncResult"></param> private static void OnSendCallBack(IAsyncResult asyncResult) Socket socket = asyncResult.AsyncState as Socket; ClientState state = clients[socket]; int count = socket.EndSend(asyncResult); byte[] ba = state.sendQueue.Peek(); if (count == ba.Length) lock (state.sendQueue) state.sendQueue.Dequeue(); ba = state.sendQueue.Count > 0 ? state.sendQueue.Peek() : null; if (ba!=null) socket.BeginSend(ba, 0, ba.Length, 0, OnSendCallBack, socket); catch (SocketException ex) Console.WriteLine("OnSendCallBack Fail:"+ex.ToString()); /// <summary> /// 检测断线 /// </summary> private static void OnTimer() if (!isUsePing) return; long nowts = GetTimeSpan(); foreach (ClientState item in clients.Values) if (nowts - item.lastPingTime>pingInterval*4) Console.WriteLine("Ping Close:"+item.socket.RemoteEndPoint.ToString()); Close(item); return; public static long GetTimeSpan() TimeSpan timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0); return Convert.ToInt64(timeSpan.TotalSeconds);

连接数据库 DBMananger

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using MySql.Data.MySqlClient;
namespace SeverManager
    public class DBManager
        public static MySqlConnection mySql;
        /// <summary>
        /// 连接数据库
        /// </summary>
        /// <param name="db"></param>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <param name="user"></param>
        /// <param name="pw"></param>
        /// <returns></returns>
        public static bool ConnectDB(string db,string ip,int port,string user,string pw)
            mySql = new MySqlConnection();
            //连接参数
            string connectStr = string.Format("Database={0};Data Source={1};Port={2};User Id={3};Password={4};", db,ip,port,user,pw);
            mySql.ConnectionString = connectStr;
                mySql.Open();
                Console.WriteLine("【数据库】connect success!");
                return true;
            catch (Exception ex)
                Console.WriteLine("【数据库】Connect Fail:"+ex.Message);
                return false;
        /// <summary>
        /// 判断是否是安全字符串
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static bool IsSafeString(string str)
            if (string.IsNullOrEmpty(str))
                return true;
            return !Regex.IsMatch(str,@"[-|;|,|.|\/|\(|\)|\{|\}|%|@|\*|!|\']");
        /// <summary>
        /// 判断是否存在用户
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static bool IsAccountExist(string col,string val)
            //防止SQL注入
            if (!DBManager.IsSafeString(val))
                Console.WriteLine("【数据库】IsAccountExist: Id is not safe");
                return false;
            if (!DBManager.IsSafeString(col))
                Console.WriteLine("【数据库】IsAccountExist: col is not safe");
                return false;
            //sql语句
            string sqlStr = string.Format("select * from account where {0} = '{1}'", col.Trim(), val.Trim());
                MySqlCommand command = new MySqlCommand(sqlStr, mySql);
                MySqlDataReader dataReader = command.ExecuteReader();
                bool hasRow = dataReader.HasRows;
                dataReader.Close();
                return hasRow;
            }catch(Exception ex)
                Console.WriteLine("【数据库】IsAccountExist ERR:"+ex.Message);
                return false;
        /// <summary>
        /// 注册用户
        /// </summary>
        /// <param name="account">用户信息</param>
        public static bool Register(Account account)
            if (!IsSafeString(account.Id))
                Console.WriteLine("【数据库】Register: Id is not safe");
                return false;
            if (!IsSafeString(account.PassWord))
                Console.WriteLine("【数据库】Register: Password is not safe");
                return false;
            if (!IsSafeString(account.NickName))
                Console.WriteLine("【数据库】Register: NickName is not safe");
                return false;
            if (IsAccountExist("Id",account.Id))
                Console.WriteLine("【数据库】Register faiil:已存在用户 id:"+account.Id);
                return false;
                string s =string.Format("insert into account set Id='{0}',PassWord='{1}',NickName='{2}',CreateTime = '{3}'",account.Id,account.PassWord,account.NickName,DateTime.Now);
                using (MySqlCommand cmd = new MySqlCommand(s, mySql))
                    cmd.ExecuteNonQuery();
                return true;
            catch (Exception ex)
                Console.WriteLine("【数据库】Register ERR:"+ex.Message);
                return false;
        /// <summary>
        /// 创建角色信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static bool CreatePlayerData(string id)
            if (!IsSafeString(id))
                Console.WriteLine("【数据库】CreatePlayerData fail: Id is not safe");
                return false;
            if (!IsAccountExist("Id",id))
                Console.WriteLine("【数据库】CreatePlayerData fail: 不存在账号信息 "+id);
                return false;
                string s = string.Format("insert into PlayerData set PlayerId='{0}',Coins={1},PlayerLevel={2},PlayerDesc='{3}'",id.Trim(),0,1,"");
                using (MySqlCommand cmd = new MySqlCommand(s, mySql))
                    cmd.ExecuteNonQuery();
                return true;
            catch (Exception ex)
                Console.WriteLine("【数据库】CreatePlayerData ERR:"+ ex.Message);
                return false;
        /// <summary>
        /// 检查用户名密码
        /// </summary>
        /// <param name="id"></param>
        /// <param name="pwd"></param>
        /// <returns></returns>
        public static bool CheckPassword(string id,string pwd)
            if (!IsSafeString(id))
                Console.WriteLine("【数据库】CheckPassword:id is not safe");
                return false;
            if (!IsSafeString(pwd))
                Console.WriteLine("【数据库】CheckPassword:pwd is not safe");
                return false;
                string s = string.Format("select * from Account where Id='{0}' and Password='{1}'",id.Trim(),pwd.Trim());
                MySqlCommand cmd = new MySqlCommand(s,mySql);
                MySqlDataReader data = cmd.ExecuteReader();
                bool hasrows = data.HasRows;
                data.Close();
                return hasrows;
            catch (Exception ex)
                Console.WriteLine("【数据库】CheckPassword ERR:"+ex.Message);
                return false;
        /// <summary>
        /// 获取玩家数据
        /// </summary>
        /// <param name="id">用户id</param>
        /// <returns></returns>
        public static PlayerData GetPlayerData(string id)
            if (!IsSafeString(id))
                Console.WriteLine("【数据库】GetPlayerData fail:id isnot safe");
                return null;
            string s = string.Format("select * from PlayerData where PlayerId = '{0}'", id.Trim());
                MySqlCommand cmd = new MySqlCommand(s,mySql);
                MySqlDataReader data = cmd.ExecuteReader();
                if (!data.HasRows)
                    return null;
                data.Read();
                string plyaerId = data["PlayerId"].ToString();
                int coins = Convert.ToInt32(data["Coins"]);
                int levl = Convert.ToInt32(data["PlayerLevel"]);
                string playerDesc = data["PlayerDesc"].ToString();
                PlayerData playerData = new PlayerData(plyaerId,coins,levl,playerDesc);
                data.Close();
                return playerData;
            catch (Exception ex) {
                Console.WriteLine("【数据库】GetPlayerData ERR:"+ex.Message);
                return null;
        /// <summary>
        /// 更新用户数据
        /// </summary>
        /// <param name="playerData"></param>
        /// <returns></returns>
        public static bool UpdatePlayerData(PlayerData playerData)
                string s = string.Format("update PlayerData set Coins = {0},PlayerLevel = {1},PlayerDesc='{2}' where PlayerId={3}",playerData.Coins,playerData.PlayerLevel,playerData.PlayerDesc);
                using (MySqlCommand cmd = new MySqlCommand(s, mySql))
                    cmd.ExecuteNonQuery();
                return true;
            catch (Exception ex)
                Console.WriteLine("【数据库】UpdatePlayerData ERR:" + ex.Message) ;
                return false;

角色管理 PlayerManger

using System;
using System.Collections.Generic;
using System.Text;
namespace SeverManager
    public class PlayerManager
        private static Dictionary<string, Player> PlayerDic = new Dictionary<string, Player>();
        /// <summary>
        /// 添加角色
        /// </summary>
        /// <param name="player"></param>
        public static void AddPlayer(Player player)
            if (!PlayerDic.ContainsKey(player.Id))
                PlayerDic.Add(player.Id,player);
        /// <summary>
        /// 移除角色
        /// </summary>
        /// <param name="id"></param>
        public static void RemovePlayer(string id)
            if (PlayerDic.ContainsKey(id))
                PlayerDic.Remove(id);
        /// <summary>
        /// 获取角色ById
        /// </summary>
        /// <param name="id"></param>
        public static Player GetPlayerById(string id)
            return PlayerDic[id];
        /// <summary>
        /// 是否在线
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static bool IsOnline(string id)
            return PlayerDic.ContainsKey(id);

服务端 MsgBase 协议基类

using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
namespace SeverManager
    public class MsgBase
        public string protoName;
        public MsgBase(string protoName)
            this.protoName = protoName;
        public static byte[] Encode(MsgBase msg)
            byte[] bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(msg));
            return bytes;
        public static MsgBase Decode(string protoName, byte[] bytes, int offset, int count)
            string s = Encoding.UTF8.GetString(bytes,offset,count);
            Type type = Type.GetType("SeverManager." + protoName);
            MsgBase msg =(MsgBase) JsonConvert.DeserializeObject(s, type);
            return msg;
        public static byte[] EncodeProtoName(string protoName)
            byte[] nameBy = Encoding.UTF8.GetBytes(protoName);
            byte[] ret = new byte[2+nameBy.Length];
            ret[0] = Convert.ToByte(nameBy.Length % 256);
            ret[1] = Convert.ToByte(nameBy.Length / 256);
            Array.Copy(nameBy,0,ret,2,nameBy.Length);
            return ret;
        public static string DecodeProtoName(byte[] data , int offset , out int count)
            count = 0;
            if (offset+2>=data.Length)
                return "";
            int len = data[offset + 1] << 8 | data[offset];
            if (len<=0)
                count = 2;
                return "";
            count = len + 2;
            string s = Encoding.UTF8.GetString(data,offset+2,len);
            return s;

ClientState 服务端监听的客户端状态

using iTextSharp.text.pdf.qrcode;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
namespace SeverManager
    public class ClientState
        public int capacity = 1024;
        public Socket socket;
        public byte[] readBuff;
        public int buffCount = 0;
        public Queue<byte[]> sendQueue;
        public long lastPingTime;
        public Player player;
        public ClientState()
            readBuff =  new byte[capacity];
            sendQueue = new Queue<byte[]>();