Browse Source

添加CSharp服务器.

gwang 3 years ago
parent
commit
4d3fb402ce
36 changed files with 6729 additions and 0 deletions
  1. 13 0
      CSserver/BossServer/BossServer.csproj
  2. 64 0
      CSserver/BossServer/Program.cs
  3. 847 0
      CSserver/BossServer/db/MysqlUtil.cs
  4. 31 0
      CSserver/BossServer/db/Redis.cs
  5. 152 0
      CSserver/BossServer/server/Boss.cs
  6. 163 0
      CSserver/BossServer/server/EmailProc.cs
  7. 242 0
      CSserver/BossServer/server/Peer.cs
  8. 288 0
      CSserver/BossServer/server/Room.cs
  9. 86 0
      CSserver/Lib1/Config.cs
  10. 12 0
      CSserver/Lib1/Lib1.csproj
  11. 112 0
      CSserver/Lib1/MemKey_User.cs
  12. 137 0
      CSserver/Lib1/utils/CompressUtil.cs
  13. 55 0
      CSserver/Lib1/utils/DateTimeExtension.cs
  14. 180 0
      CSserver/Lib1/utils/HttpHelper.cs
  15. 53 0
      CSserver/Lib1/utils/Singleton.cs
  16. 26 0
      CSserver/Lib1/utils/SocketUtils.cs
  17. 40 0
      CSserver/Lib1/utils/TaskWaitingExtension.cs
  18. 170 0
      CSserver/PBReferens/DataBuff.cs
  19. 16 0
      CSserver/PBReferens/PBReferens.csproj
  20. 8 0
      CSserver/PBReferens/pb/ErrCode.proto
  21. 33 0
      CSserver/PBReferens/pb/MsgTypeEnum.proto
  22. 58 0
      CSserver/PBReferens/pb/chat.proto
  23. 55 0
      CSserver/PBReferens/pb/fight.proto
  24. 725 0
      CSserver/PBReferens/pbcs/Chat.cs
  25. 41 0
      CSserver/PBReferens/pbcs/ErrCode.cs
  26. 1131 0
      CSserver/PBReferens/pbcs/Fight.cs
  27. 91 0
      CSserver/PBReferens/pbcs/MsgTypeEnum.cs
  28. 252 0
      CSserver/clientTest/Program.cs
  29. 313 0
      CSserver/clientTest/Program_Chat.cs
  30. 18 0
      CSserver/clientTest/clientTest.csproj
  31. 55 0
      CSserver/csserver.sln
  32. 13 0
      CSserver/csserver/ChatServer.csproj
  33. 174 0
      CSserver/csserver/Program.cs
  34. 847 0
      CSserver/csserver/db/MysqlUtil.cs
  35. 31 0
      CSserver/csserver/db/Redis.cs
  36. 197 0
      CSserver/csserver/server/Peer.cs

+ 13 - 0
CSserver/BossServer/BossServer.csproj

@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Lib1\Lib1.csproj" />
+    <ProjectReference Include="..\PBReferens\PBReferens.csproj" />
+  </ItemGroup>
+
+</Project>

+ 64 - 0
CSserver/BossServer/Program.cs

@@ -0,0 +1,64 @@
+using BossServer.server;
+using System;
+using System.Diagnostics;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace BossServer
+{
+
+    class Program
+    {
+        static Socket SocketWatch = null;                                       // 创建一个和客户端通信的套接字 
+        const int port = 6002;                                                  // 端口号(用来监听的) 
+
+        static void Main(string[] args)
+        {
+            selfTest();
+            IPAddress ip = IPAddress.Any;
+            IPEndPoint ipe = new IPEndPoint(ip, port);                         //将IP地址和端口号绑定到网络节点point上  
+
+            //定义一个套接字用于监听客户端发来的消息,包含三个参数(IP4寻址协议,流式连接,Tcp协议)  
+            SocketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+            SocketWatch.Bind(ipe);                                             // 监听绑定的网络节点           
+            SocketWatch.Listen(20);                                            // 将套接字的监听队列长度限制为20    
+
+            Console.WriteLine("开启监听......");
+            Console.WriteLine("ctrl + c 退出程序......");
+
+            Task.Run(WatchConnecting).Wait();                                  // 开启监听线程
+            SocketWatch.Close();                                               // 结束监听socket
+
+        }
+
+        static void selfTest()
+        {
+            var rdb = Redis.Ins.GetDatabase(0);
+            Debug.Assert(rdb.StringSet("test", "wanggang" + DateTime.Now.ToString("yyyyMMddHHmmss")), "Redis读写失败!");
+        }
+
+        /// <summary>
+        /// 监听客户端发来的请求  
+        /// </summary>
+        async static Task WatchConnecting()
+        {
+            while (true)                                                                 // 持续不断监听客户端发来的请求     
+            {
+                Socket connection;
+                try
+                {
+                    connection = await SocketWatch.AcceptAsync();
+                }
+                catch (Exception ex)
+                {
+                    Console.WriteLine(ex.Message);                                       // 提示套接字监听异常     
+                    break;
+                }
+
+                Room.Instance.AddPeer(new Peer(connection));
+            }
+        }
+
+    }
+}

+ 847 - 0
CSserver/BossServer/db/MysqlUtil.cs

@@ -0,0 +1,847 @@
+using MySql.Data.MySqlClient;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Diagnostics;
+using static System.Diagnostics.Trace;
+using static System.String;
+
+namespace BossServer.db
+{
+    /// <summary>
+    /// MySQL操作辅助类.
+    /// </summary>
+    public class MysqlUtil : IDisposable
+    {
+
+        /// <summary>
+        /// MySQL字段详细信息,(用于GetFiledInfo方法返回值中取信息,可以用nameof来去掉硬编码字符串以防止拼写错误)
+        /// </summary>
+        public enum EFieldInfo
+        {
+            /// <summary>
+            /// 字段类型 in .net
+            /// </summary>
+            Type,
+            /// <summary>
+            /// 是否可空
+            /// </summary>
+            Null,
+            /// <summary>
+            /// 索引类型
+            /// </summary>
+            Key,
+            /// <summary>
+            /// 默认值
+            /// </summary>
+            Default,
+            /// <summary>
+            /// 注释
+            /// </summary>
+            Comment,
+            /// <summary>
+            /// 字符集
+            /// </summary>
+            Collation,
+            /// <summary>
+            /// 额外(自增等)
+            /// </summary>
+            Extra,
+            /// <summary>
+            /// 权限
+            /// </summary>
+            Privileges
+        }
+
+        #region `    基础代码   `
+        static MysqlUtil mInst;
+        public static MysqlUtil Ins
+        {
+            get
+            {
+                if (null == mInst)
+                {
+                    mInst = new MysqlUtil();
+                }
+                return mInst;
+            }
+        }
+
+        private MysqlUtil()
+        {
+            conn = new MySqlConnection(Config.Ins.mysql.ConnectionString);
+            conn.Open();
+            Assert(conn.State == ConnectionState.Open, "连接到数据库失败");
+        }
+  
+        private MySqlConnection conn = null;
+        /// <summary>
+        /// 建立连接
+        /// </summary>
+        /// <param name="host"></param>
+        /// <param name="port"></param>
+        /// <param name="user"></param>
+        /// <param name="pwd"></param>
+        /// <param name="dbName"></param>
+        public void Connect(string host, string port, string user, string pwd, string dbName)
+        {
+            var connStr = string.Format(
+                              "server={0};port={1};user={2};password={3};database={4};Charset=utf8",
+                              host, port, user, pwd, dbName);
+            conn = new MySqlConnection(connStr);
+            conn.Open();
+            Assert(conn.State == ConnectionState.Open, "连接到数据库失败");
+        }
+        /// <summary>
+        /// 关闭连接
+        /// </summary>
+        public void Close()
+        {
+            conn.Close();
+        }
+        /// <summary>
+        /// 释放资源
+        /// </summary>
+        public void Dispose()
+        {
+            conn.Dispose();
+        }
+        #endregion
+
+        #region `    数据读    `
+
+        /// <summary>
+        /// 读取数据库表-数据操作部分逻辑需要参数中定义
+        /// 一般情况下,使用其余八个读取函数即可,如果需要自己定制数据结构,可以通过在此函数回调中自行遍历整个数据表.
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="working">action to MysqlDataReader</param>
+        public void ReadTabel(string tableName, Action<MySqlDataReader> working)
+        {
+            Assert(TableExist(tableName), "表 " + tableName + " 不存在!");
+            var sql = string.Format("SELECT * FROM {0};", tableName);
+            using (var cmd = new MySqlCommand(sql, conn))
+            using (var rdr = cmd.ExecuteReader())
+            {
+                if (null != working) working.Invoke(rdr);
+            }
+        }
+
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个JArray中, []
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// /// <param name="excludeFields">排除的字段名列表</param>
+        /// <returns>JArray</returns>
+        public JArray ReadTabelToJArray(string tableName, params string[] excludeFields)
+        {
+            var ex = new List<string>(excludeFields);
+            var arr = new JArray();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var jobj = new JObject();
+
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            jobj.Add(rdr.GetName(i), JToken.FromObject(rdr.GetValue(i)));
+                        }
+                    }
+                    arr.Add(jobj);
+                }
+            });
+            return arr;
+        }
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个JObject中, {}
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="indexFieldName">用做索引的字段名</param>
+        /// <param name="excludeFields">排除的字段名列表</param>
+        /// <returns>{}</returns>
+        public JObject ReadTabelToJObject(string tableName, string indexFieldName, out bool isArray, params string[] excludeFields)
+        {
+            Assert(HasFiledInTable(tableName, indexFieldName),
+                Format("在表[{0}]中未找到key:[{1}]", tableName, indexFieldName));
+            bool _isArray = false;
+            var ex = new List<string>(excludeFields);
+            //ex.Add(indexFieldName);
+            var obj = new JObject();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var jobj = new JObject();
+                    var id = "";
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == indexFieldName)
+                        {
+                            id = rdr.GetValue(i).ToString();
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            object v = rdr.GetValue(i);
+
+                            if (DBNull.Value == v)
+                            {
+                                var t = rdr.GetFieldType(i).Name;
+                                switch (t)
+                                {
+                                    case "Int32":
+                                        v = 0;
+                                        break;
+                                    case "String":
+                                        v = "";
+                                        break;
+                                    case "Single":
+                                        v = 0;
+                                        break;
+                                }
+                            }
+                            jobj.Add(rdr.GetName(i), JToken.FromObject(v));
+                        }
+                    }
+                    if (obj[id] == null)
+                    {  // 这段代码智能转换多值对应为[]
+                        obj.Add(id, jobj);
+                    }
+                    else
+                    {
+                        _isArray = true;
+                        var arr = obj[id] as JArray;
+                        if (null != arr)
+                        {
+                            arr.Add(jobj);
+                        }
+                        else
+                        {
+                            obj[id] = new JArray() { obj[id], jobj };
+                        }
+                    }
+                }
+
+            });
+            if (_isArray)
+            {
+                foreach (var o in obj.Properties())
+                {
+                    if (null == (o.Value as JArray))
+                    {
+                        obj[o.Name] = new JArray() { o.Value };
+                    }
+                }
+            }
+            isArray = _isArray;
+            return obj;
+        }
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个JObject中, {k->[],k->[],...}
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="indexFieldName">用做索引的字段名</param>
+        /// <param name="excludeFields">排除的字段名列表</param>
+        /// <returns>{}</returns>
+        public JObject ReadTabelToJObjectWithArrayValues(string tableName, string indexFieldName, params string[] excludeFields)
+        {
+            Assert(HasFiledInTable(tableName, indexFieldName),
+                Format("在表[{0}]中未找到key:[{1}]", tableName, indexFieldName));
+
+            var ex = new List<string>(excludeFields);
+            //ex.Add(indexFieldName);
+            var obj = new JObject();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var jobj = new JObject();
+                    var id = "";
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == indexFieldName)
+                        {
+                            id = rdr.GetValue(i).ToString();
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            object v = rdr.GetValue(i);
+
+                            if (DBNull.Value == v)
+                            {
+                                var t = rdr.GetFieldType(i).Name;
+                                switch (t)
+                                {
+                                    case "Int32":
+                                        v = 0;
+                                        break;
+                                    case "String":
+                                        v = "";
+                                        break;
+                                    case "Single":
+                                        v = 0;
+                                        break;
+                                }
+                            }
+                            jobj.Add(rdr.GetName(i), JToken.FromObject(v));
+                        }
+                    }
+
+                    var arr = obj[id] as JArray;
+                    if (null != arr)
+                    {
+                        arr.Add(jobj);
+                    }
+                    else
+                    {
+                        obj[id] = new JArray() { jobj };
+                    }
+
+                }
+
+            });
+
+            return obj;
+        }
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个简单的key=>value结构中. 即只取表中的两个字段
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="keyName">用作属性名称的字段</param>
+        /// <param name="valueName">用作值的字段</param>
+        /// <returns>{k:v,k:v,...}</returns>
+        public JObject ReadTabelToSimpleJObject(string tableName, string keyName, string valueName)
+        {
+            Assert(HasFiledInTable(tableName, keyName),
+                    Format("在表[{0}]中未找到key:[{1}]", tableName, keyName));
+            Assert(HasFiledInTable(tableName, valueName),
+                Format("在表[{0}]中未找到字段:[{1}]", tableName, valueName));
+            var jobj = new JObject();
+
+            ReadTabel(tableName, rdr =>
+           {
+               while (rdr.Read())
+               {
+                   var key = "";
+                   Object value = null;
+                   for (int i = 0; i < rdr.FieldCount; i++)
+                   {
+                       if (rdr.GetName(i) == keyName)
+                       {
+                           key = rdr.GetValue(i).ToString();
+                       }
+                       if (rdr.GetName(i) == valueName)
+                       {
+                           value = rdr.GetValue(i);
+                       }
+                   }
+                   jobj.Add(key, JToken.FromObject(value));
+               }
+           });
+
+            return jobj;
+        }
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个由两个字段内容拼接成的字符串作为索引的JObject中
+        /// </summary>
+        /// <param name="tableName">表名称</param>
+        /// <param name="firstFieldName">第一个字段</param>
+        /// <param name="secondFieldName">第二个字段</param>
+        /// <param name="excludeFields">排除的字段名...</param>
+        /// <returns> {field(1)-field(2):{},...} </returns>
+        public JObject ReadTabelToJObjectWithCombinedIndex(string tableName, string firstFieldName, string secondFieldName, out bool isArray, params string[] excludeFields)
+        {
+            Assert(HasFiledInTable(tableName, firstFieldName),
+                 Format("在表[{0}]中未找到字段:[{1}]", tableName, firstFieldName));
+            Assert(HasFiledInTable(tableName, secondFieldName),
+                Format("在表[{0}]中未找到字段:[{1}]", tableName, secondFieldName));
+            bool _isArray = false;
+            var ex = new List<string>(excludeFields);
+            //ex.Add(firstFieldName);
+            //ex.Add(secondFieldName);
+            var obj = new JObject();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var jobj = new JObject();
+                    var firstId = "";
+                    var secondId = "";
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == firstFieldName)
+                        {
+                            firstId = rdr.GetValue(i).ToString();
+                        }
+                        if (rdr.GetName(i) == secondFieldName)
+                        {
+                            secondId = rdr.GetValue(i).ToString();
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            Object v = rdr.GetValue(i);
+                            if (DBNull.Value == v)
+                            {
+                                var t = rdr.GetFieldType(i).Name;
+                                switch (t)
+                                {
+                                    case "Int32":
+                                        v = 0;
+                                        break;
+                                    case "String":
+                                        v = "";
+                                        break;
+                                    case "Single":
+                                        v = 0;
+                                        break;
+                                }
+                            }
+                            jobj.Add(rdr.GetName(i), JToken.FromObject(v));
+                        }
+                    }
+                    var key = firstId + "-" + secondId;
+                    if (obj[key] == null)
+                    {  // 防止重复
+                        obj.Add(key, jobj);
+                    }
+                    else
+                    {  // 已有对象 => 智能转换多值对应为[]
+                        _isArray = true;
+                        var arr = obj[key] as JArray;
+                        if (arr != null)
+                        { // 已经是jarray
+                            arr.Add(jobj);
+                        }
+                        else
+                        {    // 创建Jarray
+                            obj[key] = new JArray() { obj[key], jobj };
+                        }
+                    }
+                }
+            });
+
+            if (_isArray)
+            {
+                foreach (var o in obj.Properties())
+                {
+                    if (null == (o.Value as JArray))
+                    {
+                        obj[o.Name] = new JArray() { o.Value };
+                    }
+                }
+            }
+            isArray = _isArray;
+            return obj;
+        }
+
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个{key11:{key1_21:{},key1_22:{},...},key12:{key2_21:{},key2_22:{},...},...}结构数据中
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="indexFieldName">用做索引的字段名</param>
+        /// <param name="secondaryIndexName">用做次级索引的字段名</param>
+        /// <param name="excludeFields">排除的字段名列表</param>
+        /// <returns>拥有二级索引的json对象,二级节点是{}</returns>
+        public JObject ReadTabelToJObjectWithSecondaryIndex(string tableName,
+            string indexFieldName, string secondaryIndexName, out bool isArray, params string[] excludeFields)
+        {
+            Assert(HasFiledInTable(tableName, indexFieldName),
+            Format("在表[{0}]中未找到字段:[{1}]", tableName, indexFieldName));
+            Assert(HasFiledInTable(tableName, secondaryIndexName),
+                Format("在表[{0}]中未找到字段:[{1}]", tableName, secondaryIndexName));
+            var _isArray = false;
+            var ex = new List<string>(excludeFields);
+            //ex.Add(indexFieldName);
+            //ex.Add(secondaryIndexName);
+            var obj = new JObject();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var jobj = new JObject();
+                    var id = "";
+                    var secondId = "";
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == indexFieldName)
+                        {
+                            id = rdr.GetValue(i).ToString();
+                        }
+                        if (rdr.GetName(i) == secondaryIndexName)
+                        {
+                            secondId = rdr.GetValue(i).ToString();
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            Object v = rdr.GetValue(i);
+                            if (DBNull.Value == v)
+                            {
+                                var t = rdr.GetFieldType(i).Name;
+                                switch (t)
+                                {
+                                    case "Int32":
+                                        v = 0;
+                                        break;
+                                    case "String":
+                                        v = "";
+                                        break;
+                                    case "Single":
+                                        v = 0;
+                                        break;
+                                }
+                            }
+                            jobj.Add(rdr.GetName(i), JToken.FromObject(v));
+                        }
+                    }
+
+                    if (obj[id] == null)
+                    {  // 没有建立一级对象
+                        var o = new JObject();
+                        o.Add(secondId, jobj);
+                        obj.Add(id, o);
+                    }
+                    else
+                    {  // 已有一级对象
+                        var o = obj[id] as JObject;  // 一级对象
+                        if (o[secondId] == null)
+                        {  // 添加新值
+                            o.Add(secondId, jobj);
+                        }
+                        else
+                        {    // 已有对象 => 智能转换多值对应为[]
+                            _isArray = true;
+                            var arr = o[secondId] as JArray;
+                            if (arr != null)
+                            { // 已经是jarray
+                                arr.Add(jobj);
+                            }
+                            else
+                            {    // 创建Jarray
+                                o[secondId] = new JArray() { o[secondId], jobj };
+                            }
+                        }
+                    }
+                }
+            });
+            if (_isArray)
+            {
+                foreach (var o in obj.Properties())
+                {
+                    foreach (var o2 in (o.Value as JObject).Properties())
+                    {
+                        if (!(obj[o.Name][o2.Name] is JArray))
+                        {
+                            obj[o.Name][o2.Name] = new JArray() { obj[o.Name][o2.Name] };
+                        }
+                    }
+                }
+            }
+            isArray = _isArray;
+            return obj;
+        }
+
+
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个行数组[{},{}]
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="excludeFields">排除的字段名列表</param>
+        /// <returns>普通行数组[{},{},...]</returns>
+        public Dictionary<string, object>[] ReadTabelToArray(string tableName, params string[] excludeFields)
+        {
+            var ex = new List<string>(excludeFields);
+            var arr = new List<Dictionary<string, object>>();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var r = new Dictionary<string, object>(rdr.FieldCount - 1);
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            r.Add(rdr.GetName(i), rdr.GetValue(i));
+                        }
+                    }
+                    arr.Add(r);
+                }
+            });
+            return arr.ToArray();
+        }
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个dic中 {"key":{}}
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="indexFieldName">仅限索引字段(1对多的情况暂不支持)</param>
+        /// <param name="excludeFields">排除的字段名列表</param>
+        /// <typeparam name="T">string, number</typeparam>
+        /// <returns>嵌套的dictionary:{k:{k,v},k:{k:v},...}</returns>
+        public Dictionary<T, Dictionary<string, object>> ReadTabelToDic_Dic<T>(string tableName, string indexFieldName, params string[] excludeFields)
+        {
+            Assert(HasFiledInTable(tableName, indexFieldName),
+                Format("在表[{0}]中未找到字段:[{1}]", tableName, indexFieldName));
+            var ex = new List<string>(excludeFields);
+            //ex.Add(indexFieldName);
+            var dic = new Dictionary<T, Dictionary<string, object>>();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var r = new Dictionary<string, object>(rdr.FieldCount - 1);
+                    var t = default(T);
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == indexFieldName)
+                        {
+                            t = (T)rdr.GetValue(i);
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            r.Add(rdr.GetName(i), rdr.GetValue(i));
+                        }
+                    }
+                    dic.Add(t, r);
+                }
+
+            });
+            return dic;
+        }
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个dic中 {"key":[]}
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="indexFieldName">索引字段名</param>
+        /// <param name="excludeFields">排除的字段名列表</param>
+        /// <typeparam name="T">string, number</typeparam>
+        /// <returns>复杂嵌套dictionary:{k:[{},{},..],k:[{},{},..]..}</returns>
+        public Dictionary<T, List<Dictionary<string, object>>> ReadTabelToDic_List<T>(string tableName, string indexFieldName, params string[] excludeFields)
+        {
+            Assert(HasFiledInTable(tableName, indexFieldName),
+                Format("在表[{0}]中未找到字段:[{1}]", tableName, indexFieldName));
+            var ex = new List<string>(excludeFields);
+            //ex.Add(indexFieldName);
+            var dic = new Dictionary<T, List<Dictionary<string, object>>>();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var r = new Dictionary<string, object>(rdr.FieldCount - 1);
+                    var t = default(T);
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == indexFieldName)
+                        {
+                            t = (T)rdr.GetValue(i);
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            r.Add(rdr.GetName(i), rdr.GetValue(i));
+                        }
+                    }
+                    if (dic.ContainsKey(t))
+                    {
+                        dic[t].Add(r);
+                    }
+                    else
+                    {
+                        dic.Add(t, new List<Dictionary<string, object>> { r });
+                    }
+                }
+            });
+            return dic;
+        }
+
+        #endregion
+
+        #region `   数据写   `
+
+        /// <summary>
+        /// 执行非查询类的SQL语句,比如插入/更新/删除之类的.
+        /// </summary>
+        /// <param name="sql"></param>
+        /// <returns></returns>
+        public int ExecuteSqlNonQuery(string sql) {
+            using var cmd = new MySqlCommand(sql, conn);
+            return cmd.ExecuteNonQuery();
+        }
+
+
+
+        #endregion
+
+        #region '    辅助方法    '
+
+        /// <summary>
+        /// 查询某表中是否存在指定字段
+        /// </summary>
+        /// <param name="tableName"></param>
+        /// <param name="fieldName"></param>
+        /// <returns></returns>
+        public bool HasFiledInTable(string tableName, string fieldName)
+        {
+            Trace.Assert(TableExist(tableName), tableName + " 未找到");
+            return GetTableFieldsInfo(tableName).ContainsKey(fieldName);
+        }
+
+        /// <summary>
+        /// 检查指定的表格是否存在
+        /// </summary>
+        /// <param name="tableName"></param>
+        /// <returns></returns>
+        public bool TableExist(string tableName)
+        {
+            try
+            {
+                new MySqlCommand("select * from " + tableName + " limit 1;", conn).ExecuteNonQuery();
+            }
+            catch (MySqlException ex)
+            {
+                switch (ex.Number)
+                {
+                    case 1146:
+                        return false;
+                    default:
+                        Debug.WriteLine(ex.Message);
+                        return false;
+                }
+            }
+            return true;
+        }
+
+        /// <summary>
+        /// 执行sql
+        /// </summary>
+        /// <param name="sql"></param>
+        public void Query(string sql)
+        {
+            try
+            {
+                using (var cmd = new MySqlCommand(sql, conn))
+                {
+                    cmd.ExecuteNonQuery();
+                }
+            }
+            catch (MySqlException ex)
+            {
+                Debug.WriteLine(ex.Message);
+            }
+        }
+        /// <summary>
+        /// 执行sql 查询
+        /// </summary>
+        /// <param name="sqlcmd"></param>
+        /// <param name="working"></param>
+        public void QueryTable(string sqlcmd, Action<MySqlDataReader> working)
+        {
+            try
+            {
+                using (var cmd = new MySqlCommand(sqlcmd, conn))
+                using (MySqlDataReader rdr = cmd.ExecuteReader())
+                {
+
+                    if (null != working)
+                    {
+                        working.Invoke(rdr);
+                    }
+                }
+            }
+            catch (MySqlException ex)
+            {
+                Debug.WriteLine(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 查询表格的字段信息  FieldName :
+        ///     <Type(字段类型 in .net)>
+        ///     <Collation(字符集及排序)>
+        ///     <Null(是否可空)>
+        ///     <Key(索引类型)>
+        ///     <Default(默认值)>
+        ///     <Extra(自增)>
+        ///     <Privileges(权限)>
+        ///     <Comment(注释)>
+        /// </summary>
+        /// <param name="tableName"></param>
+        /// <returns></returns>
+        public Dictionary<string, Dictionary<string, object>> GetTableFieldsInfo(string tableName)
+        {
+            var ex = new List<string>();
+            var dic = new Dictionary<string, Dictionary<string, object>>();
+            QueryTable(string.Format("show full fields from {0}", tableName), rdr =>
+            {
+                while (rdr.Read())
+                {
+
+                    var r = new Dictionary<string, object>();
+                    var fieldName = rdr.GetString(0);
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == "Field")
+                        {
+                            fieldName = rdr.GetString(i);
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            r.Add(rdr.GetName(i), rdr.GetValue(i));
+                        }
+
+                    }
+                    dic.Add(fieldName, r);
+                }
+            });
+            ReadTabel(tableName, rdr =>               // 用。net类型替换掉myslq 类型字符串
+            {
+                for (var i = 0; i < rdr.FieldCount; i++)
+                {
+
+                    var filedName = rdr.GetName(i);               // 字段名
+                    var filedType = rdr.GetFieldType(i).Name;     // 字段类型
+                    if (dic.ContainsKey(filedName))
+                    {
+                        dic[filedName][nameof(EFieldInfo.Type)] = filedType;
+                    }
+                }
+            });
+            return dic;
+        }
+
+        /// <summary>
+        /// 查询表格的最后修改时间
+        /// </summary>
+        /// <param name="tableName"></param>
+        /// <returns>最后修改时间或者创建时间, 异常: 返回当前时间</returns>
+        public DateTime TableLastModified(string tableName)
+        {
+            var sql = string.Format("SELECT UPDATE_TIME, CREATE_TIME " +
+                      "FROM information_schema.tables " +
+                      "where TABLE_SCHEMA='{0}' and TABLE_NAME='{1}'",
+                          this.conn.Database, tableName);
+            using (var cmd = new MySqlCommand(sql, conn))
+            using (MySqlDataReader rdr = cmd.ExecuteReader())
+            {
+                if (rdr.Read())
+                {  // 上次更新时间为null的情况下使用表的创建时间,都找不到值的情况下返回当前时间   
+                    return rdr.IsDBNull(0) ? (rdr.IsDBNull(1) ? DateTime.Now : rdr.GetDateTime(1)) : rdr.GetDateTime(0);
+                }
+            }
+            throw new Exception("没有找到对应的表");
+        }
+        #endregion
+
+
+
+    }
+}

+ 31 - 0
CSserver/BossServer/db/Redis.cs

@@ -0,0 +1,31 @@
+using StackExchange.Redis;
+
+namespace BossServer
+{
+    class Redis
+    {
+        private static ConnectionMultiplexer redis;
+
+        static public ConnectionMultiplexer Ins
+        {
+            get
+            {
+                if (null == redis)
+                {
+                    redis = ConnectionMultiplexer.Connect(Config.Ins.redis);
+                }
+                return redis;
+            }
+        }
+        /// <summary>
+        /// 获取操作redis数据库的接口对象
+        /// </summary>
+        /// <param name="id">指定db的编号</param>
+        /// <returns></returns>
+        public static IDatabase Rdb(int id = 0)
+        {
+            return Ins.GetDatabase(id);
+        }
+
+    }
+}

+ 152 - 0
CSserver/BossServer/server/Boss.cs

@@ -0,0 +1,152 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Threading.Channels;
+
+using pb = global::Google.Protobuf;
+
+namespace BossServer.server
+{
+
+    /// <summary>
+    /// Boss对象
+    /// </summary>
+    class Boss
+    {
+        /// <summary>
+        /// 当前血量
+        /// </summary>
+        public volatile int Hp;
+        /// <summary>
+        /// 最大血量
+        /// </summary>
+        public readonly int MaxHp;
+
+        public int CountDownSecs;
+
+        /// <summary>
+        /// boss ID
+        /// </summary>
+        public int Id;
+
+        public Dictionary<int, Peer> Peers;
+
+        public Channel<int> DamageQueue;
+
+
+        private Action<Boss> _Settle;
+
+        private readonly Timer timer;
+
+        /// <summary>
+        /// 倒计时模块
+        /// </summary>
+        /// <param name="stateObject"></param>
+        private void OnTimer(object stateObject)
+        {
+            CountDownSecs--;
+        }
+
+        /// <summary>
+        /// 战斗结束
+        /// </summary>
+        public bool Ended { get; private set; } = false;
+
+        public Boss(int id, int maxHp, Action<Boss> Settle)
+        {
+            this.Id = id;
+            this.Hp = this.MaxHp = maxHp;
+            this.CountDownSecs = Config.Ins.CountDownTimes;
+            var roomName = Room.Instance.Name ?? "Room";
+            var redis_key = MemKey_Game.BossFight_Damage_byDateHour_zset(Convert.ToInt32(Id), roomName);
+            var rdb = Redis.Ins.GetDatabase(0);
+            rdb.KeyDelete(redis_key);
+            this.Peers = new Dictionary<int, Peer>();
+            this.DamageQueue = Channel.CreateBounded<int>(100);
+            this._Settle = Settle;
+            var t = Task.Run(Damage);
+            this.timer = new Timer(OnTimer, null, 0, 1000);       // 1秒1次
+            var end = Task.Delay(TimeSpan.FromSeconds(Config.Ins.CountDownTimes)).ContinueWith(t =>      // 10分钟结束战斗
+            {
+                this.Settle();
+            });
+        }
+
+
+
+
+
+        private async Task<int> Damage()
+        {
+            while (Hp > 0)
+            {
+                var damage = await DamageQueue.Reader.ReadAsync();
+                Hp += damage;
+                if (Hp < 0)
+                {
+                    Hp = 0;
+                }
+                CheckHp();
+            }
+            return 0;
+        }
+        public void CheckHp()
+        {
+            if (Hp > 0)
+            {
+                BroadUpdate();
+            }
+            else
+            {
+                Settle();
+            }
+        }
+
+        private void Settle()
+        {
+            if (!this.Ended)
+            {
+                this.Ended = true;
+                this.CountDownSecs = Config.Ins.CountDownTimes;
+                BroadGameOver();
+                this?._Settle(this);
+
+            }
+        }
+
+        private void BroadGameOver()
+        {
+
+            Console.WriteLine("游戏结束1");
+            this.Broadcast(TargetType.All, eProtocalCommand.ScGameOver, new SCGameOver() { BossHp = this.Hp, CountDown = this.CountDownSecs });
+            this.Peers.Values.ToList().ForEach(p => p.Close());
+        }
+
+        private void BroadUpdate()
+        {
+            this.Broadcast(TargetType.All, eProtocalCommand.ScUpdateProperties, new SCUpdateProperties() { BossMaxHp = this.MaxHp, BossHp = this.Hp, CountDown = this.CountDownSecs });
+        }
+
+        /// <summary>
+        /// 广播消息
+        /// </summary>
+        /// <param name="targetType"></param>
+        /// <param name="msgType"></param>
+        /// <param name="msg"></param>
+        private void Broadcast(TargetType targetType, eProtocalCommand msgType, pb::IMessage msg)
+        {
+            switch (targetType)
+            {
+                case TargetType.All:
+                    this.Peers.Values.ToList().ForEach(p => p.SendEvent(msgType, msg));
+                    break;
+                case TargetType.Others:
+                    break;
+            }
+
+        }
+    }
+}

+ 163 - 0
CSserver/BossServer/server/EmailProc.cs

@@ -0,0 +1,163 @@
+using BossServer.db;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Diagnostics;
+using CSharpUtil.Extensions.Time;
+
+namespace BossServer.server
+{
+    /// <summary>
+    /// 邮件类型枚举
+    /// </summary>
+    public enum EMailType
+    {
+        /// <summary>
+        /// 系统邮件
+        /// </summary>
+        SystemMail = 1,
+        /// <summary>
+        /// 好友雇佣 
+        /// </summary>
+        [Obsolete]
+        HireCoin,
+        /// <summary>
+        /// PVP阶段奖励
+        /// </summary>
+        [Obsolete]
+        PvpLeagueReward,
+        /// <summary>
+        /// pvp上榜奖励
+        /// </summary>
+        PvpRankReward,
+        /// <summary>
+        /// 公会申请被拒
+        /// </summary>
+        [Obsolete]
+        GuildApplyRefuse,
+        /// <summary>
+        /// 公会捐献结算
+        /// </summary>
+        [Obsolete]
+        GuildDonateSettle,
+        /// <summary>
+        /// 公会钻石礼包
+        /// </summary>
+        [Obsolete]
+        GuildCashGiftReward,
+        /// <summary>
+        /// 公会公告/通知邮件
+        /// </summary>
+        [Obsolete]
+        GuildNotice
+
+    }
+
+    /// <summary>
+    /// 邮件发送功能
+    /// </summary>
+    class EmailProc
+    {
+
+        /// <summary>
+        /// 邮件最大容量100封
+        /// </summary>
+        internal const int MaxMailContent = 100;
+
+        /// <summary>
+        /// 邮件最大过期时间7天
+        /// </summary>
+        internal const int MaxMailExpireTs = 604800;
+
+        /// <summary>
+        /// Mysql邮件日志表
+        /// </summary>
+        const string MailLog_TableName = "tab_mailrecord";
+        /// <summary>
+        /// 系统邮件 - 发送世界boss战奖励邮件
+        /// </summary>
+        /// <param name="zoneid"></param>
+        /// <param name="uid"></param>
+        /// <param name="index"></param>
+        /// <param name="rewardName"></param>
+        /// <param name="reward"></param>
+        public static void SendWorldBossReward(int zoneid, string uid, int index,string rewardName, string reward)
+        {
+            var mail = new Email(EMailType.SystemMail, "世界boss奖励", $"恭喜您在世界boss战斗中输出伤害排{index}名,获得{rewardName}奖励!", reward);
+            InsertMail(zoneid, uid, mail);
+        }
+        /// <summary>
+        /// 插入邮件
+        /// </summary>
+        /// <param name="zoneid"></param>
+        /// <param name="uid"></param>
+        /// <param name="mail"></param>
+        private static int InsertMail(int zoneid, string uid, Email mail)
+        {
+            var mem = Redis.Ins.GetDatabase(0);
+            var key_id = MemKey_User.Mail_CurId_int(zoneid, uid);
+            var key_queue = MemKey_User.Mail_Queue_hash(zoneid, uid);
+            mail.insertts = (int)DateTime.Now.ToUnixTimeStamp();
+            mail.mailId = (int)mem.StringIncrement(key_id);
+            if (!mem.HashSet(key_queue, mail.mailId.ToString(), mail.ToString()))
+            {                 // 重试下
+                mail.mailId = (int)mem.StringIncrement(key_id);
+                if (!mem.HashSet(key_queue, mail.mailId.ToString(), mail.ToString()))
+                {
+
+                }
+            }
+            logMail(zoneid, uid, mail);                                   // 将邮件写入Mysql中
+            return mail.mailId;
+        }
+        /// <summary>
+        /// 写邮件日志
+        /// </summary>
+        /// <param name="zoneid"></param>
+        /// <param name="uid"></param>
+        /// <param name="mail"></param>
+        private static void logMail(int zoneid, string uid, Email mail)
+        {
+            var sql = $" Insert Into {MailLog_TableName} (`mailId`, `zoneid`, `appendix`, `type`, `sender_name`, `sender_uid`, `to_uid`, `title`, `content`, `tag`) " +
+                $"values({mail.mailId},{zoneid},{mail.appendix}, {mail.type}, {mail.sender_name}, {mail.sender_uid}, {uid}, {mail.title}, {mail.content}, {mail.tag})";
+
+            var n = MysqlUtil.Ins.ExecuteSqlNonQuery(sql);
+            Debug.Assert(n > 0, $"{sql} 执行失败!");
+        }
+
+    }
+
+    public class Email
+    {
+        public int mailId = 0;
+        public string sender_uid;
+        public string sender_name;
+        public string appendix;
+        public int type;
+        public string title;
+        public string content;
+        public object tag;
+        public int readts = 0;
+        public int drawedts = 0;
+        public int insertts = 0;
+        public bool isDrawed() => drawedts > 0;
+        public bool isExistsReward() => !string.IsNullOrEmpty(appendix);
+        public int ExpireTs() => insertts + EmailProc.MaxMailExpireTs;
+
+
+        public Email(EMailType emailType,string title, string content,string reward,string uid_sender="系统",string name_sender="系统") {
+            this.type =(int) emailType;
+            this.title = title;
+            this.content = content;
+            this.appendix = reward;
+            this.sender_uid = uid_sender;
+            this.sender_name = name_sender;
+
+
+        }
+
+        public override string ToString()
+        {
+            return new JObject(this).ToString();
+        }
+    }
+}

+ 242 - 0
CSserver/BossServer/server/Peer.cs

@@ -0,0 +1,242 @@
+using StackExchange.Redis;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Sockets;
+using System.Threading.Channels;
+using System.Threading.Tasks;
+using ProtoDataBuff;
+using pb = global::Google.Protobuf;
+namespace BossServer.server
+{
+    /// <summary>
+    /// 客户端状态
+    /// </summary>
+    enum ClientState
+    {
+        /// <summary>
+        /// 已连接,未登录
+        /// </summary>
+        Connected,
+        /// <summary>
+        /// 已登录
+        /// </summary>
+        Authenticated,
+        /// <summary>
+        /// 已离开
+        /// </summary>
+        Leaved
+    }
+
+
+    /// <summary>
+    /// 一个连接对象的抽象,代表一个远端的对象
+    /// </summary>
+    class Peer
+    {
+        /// <summary>
+        /// 玩家属性字段
+        /// </summary>
+        public Dictionary<string, object> Properties = new();
+
+        /// <summary>
+        /// 客户端状态字段
+        /// </summary>
+        public ClientState CurrentState = ClientState.Connected;
+
+
+        /// <summary>
+        /// peer的唯一Id
+        /// </summary>
+        public readonly int Id;
+
+        /// <summary>
+        /// socket连接
+        /// </summary>
+        public readonly Socket Sock;
+
+        public string UID
+        {
+            get
+            {
+                if (Properties.TryGetValue(PropertyName.Uid, out var uid))
+                {
+                    return uid.ToString();
+                }
+                return "";
+            }
+        }
+        public int zoneid
+        {
+            get
+            {
+                if (Properties.TryGetValue(PropertyName.Zoneid, out var zoneid))
+                {
+                    return Convert.ToInt32(zoneid);
+                }
+                return 1;
+            }
+
+        }
+
+        /// <summary>
+        /// 线程安全的peer计数器
+        /// </summary>
+        private volatile static int _UniqPeerId = 0;
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="sock"></param>
+        public Peer(Socket sock)
+        {
+            this.Sock = sock;
+            Sock.ReceiveTimeout = 1500;                                         // 接收等待超时时间设为1.5秒 
+            Id = _UniqPeerId++;
+            var t = Task.Run(() => recv(Sock));                                 // 新建接收线程 
+            var tcs = Task.Run(WriteToClient);                                  // 向客户端发送消息线程
+        }
+
+
+        public void Close()
+        {
+            this.CurrentState = ClientState.Leaved;                              // 关闭接收窗口
+            Room.Instance.RemovePeer(this);                                      // 从房间中移除自己
+            //SaveDamage();
+            this.sendDataBuffer.Reader.Completion
+                .ContinueWith(t =>
+                Task.Delay(5000).ContinueWith(t1 => Sock.Close())
+                );                                                               // 延迟5秒关闭套接字
+        }
+        public void AddDamage(int damage)
+        {
+            if (Properties.ContainsKey(PropertyName.TotalDamage))
+            {
+                var totalDamage = Convert.ToInt32(Properties[PropertyName.TotalDamage]);
+                Properties[PropertyName.TotalDamage] = damage + totalDamage;
+            }
+            else
+            {
+                Properties.Add(PropertyName.TotalDamage, damage);
+            }
+            SaveDamage(damage);
+        }
+        void SaveDamage(int damage)
+        {
+            try
+            {
+                var rdb = Redis.Ins.GetDatabase(0);
+                if (Properties.TryGetValue(PropertyName.Name, out var name)
+                    && Properties.TryGetValue(PropertyName.Zoneid, out var zoneid))
+                {
+                    var roomName = Room.Instance.Name ?? "Room";
+                    var redis_key = MemKey_Game.BossFight_Damage_byDateHour_zset(Convert.ToInt32(zoneid), roomName);
+                    var who = UID + "__" + name.ToString();
+                    rdb.SortedSetIncrement(redis_key, who, damage);
+                }
+                else
+                {
+                    Console.WriteLine("uid/zoneid 为空.");
+                }
+            }
+            catch (RedisTimeoutException te) {
+                Console.WriteLine("redis操作超时");
+            }
+        }
+
+        /// <summary>
+        /// 向客户端发送事件
+        /// </summary>
+        public void SendEvent(eProtocalCommand msgType, pb::IMessage msg)
+        {
+            SendToClient(msgType, msg);
+        }
+
+        /// <summary>
+        /// 向客户端发送消息
+        /// </summary>
+        /// <param name="msgType"></param>
+        /// <param name="msg"></param>
+        void SendToClient(eProtocalCommand msgType, pb::IMessage msg)
+        {
+            using var ms = new MemoryStream();
+            using var os = new pb::CodedOutputStream(ms);
+            msg.WriteTo(os);
+            os.Flush();
+            ms.Seek(0, SeekOrigin.Begin);
+            var sdata = sSocketData.FromBytes(msgType, ms.ToArray());
+            sendDataBuffer.Writer.WriteAsync(sdata).AsTask().Wait();
+        }
+
+
+        /// <summary>
+        /// 向客户端写入消息
+        /// </summary>
+        async void WriteToClient()
+        {
+            while (true)
+            {
+                try
+                {
+                    var msg = await sendDataBuffer.Reader.ReadAsync();
+                    await Sock.SendAsync(new ArraySegment<byte>(msg.ToBytes()), SocketFlags.None);
+                }
+                catch (Exception)
+                {
+                    break;
+                }
+
+            }
+            Close();
+        }
+
+
+        /// <summary>
+        /// 发送buffer
+        /// </summary>
+        private Channel<sSocketData> sendDataBuffer = Channel.CreateUnbounded<sSocketData>();
+
+        /// <summary>
+        /// 接收客户端发来的信息,客户端套接字对象
+        /// </summary>
+        /// <param name="socketclientpara"></param>    
+        async void recv(Socket socketServer)
+        {
+
+            var _databuffer = new DataBuffer();
+            byte[] arrServerRecMsg = new byte[4096];                                       // 创建一个内存缓冲区,其大小为4k字节  
+            while (true)
+            {
+                try
+                {
+                    var length = await socketServer.ReceiveAsync(new ArraySegment<byte>(arrServerRecMsg), SocketFlags.None);   // 将接收到的信息存入到内存缓冲区,并返回其字节数组的长度    
+                    if (length <= 0 || CurrentState == ClientState.Leaved)                                                     // 视为客户端已经close连接
+                    {
+                        break;
+                    }
+                    _databuffer.AddBuffer(arrServerRecMsg, length);                                  //将收到的数据添加到缓存器中
+                    while (_databuffer.GetData(out sSocketData _socketData))                         //取出一条完整数据
+                    {
+                        await Room.Instance.MsgChannel.Writer.WriteAsync(new KeyValuePair<int, sSocketData>(this.Id, _socketData));    // 放入channel
+                    }
+                    Array.Clear(arrServerRecMsg, 0, length);
+                }
+                catch (SocketException e)
+                {
+                    if (e.ErrorCode == 10060)                                          // 超时的时候错误号码是10060
+                    {
+                        continue;                                                      // 继续等待  
+                    }
+                    break;
+                }
+                catch (Exception)
+                {
+                    break;
+                }
+            }
+
+            Close();                                                                 // 关闭
+        }
+
+    }
+}

+ 288 - 0
CSserver/BossServer/server/Room.cs

@@ -0,0 +1,288 @@
+using CSharpUtil;
+using CSharpUtil.Net;
+using ProtoDataBuff;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Channels;
+using System.Threading.Tasks;
+using static System.Console;
+
+namespace BossServer.server
+{
+    /// <summary>
+    /// 房间状态
+    /// </summary>
+    enum RoomState
+    {
+        /// <summary>
+        /// 开启状态(可以连接登入)
+        /// </summary>
+        Open,
+        /// <summary>
+        /// 已关闭(不可继续登录)
+        /// </summary>
+        Close,
+    }
+
+    enum TargetType
+    {
+        /// <summary>
+        /// 所有
+        /// </summary>
+        All,
+        /// <summary>
+        /// 其他人
+        /// </summary>
+        Others,
+
+
+    }
+    class PropertyName
+    {
+        public const string Uid = nameof(Uid);
+        public const string Name = nameof(Name);
+        public const string Zoneid = nameof(Zoneid);
+        public const string Hp = nameof(Hp);
+        public const string MaxHp = nameof(MaxHp);
+        public const string TotalDamage = nameof(TotalDamage);
+    }
+
+    /// <summary>
+    /// 房间对象
+    /// </summary>
+    class Room : Singleton<Room>
+    {
+
+        /// <summary>
+        /// 房间状态字段
+        /// </summary>
+        public RoomState CurrentState = RoomState.Close;
+
+        /// <summary>
+        /// 客户端
+        /// </summary>
+        public Dictionary<int, Peer> ClientPeers = new Dictionary<int, Peer>();
+        /// <summary>
+        /// 消息分发
+        /// </summary>
+        private Dictionary<eProtocalCommand, Action<int, sSocketData>> callbacks = new Dictionary<eProtocalCommand, Action<int, sSocketData>>();
+
+        /// <summary>
+        /// 客户端消息队列
+        /// </summary>
+        public Channel<KeyValuePair<int, sSocketData>> MsgChannel = Channel.CreateBounded<KeyValuePair<int, sSocketData>>(1000);
+
+
+        public string Name => DateTime.Now.ToString("yyyyMMddHH");
+
+        public int MaxHp => Config.GameOnline ? 100000 : 10000;       // 初始血量10w
+
+
+        /// <summary>
+        /// boss集合
+        /// </summary>
+        private Dictionary<int, Boss> Boss_dict = new();
+
+
+        /// <summary>
+        /// 构造函数
+        /// </summary> 
+        public Room()
+        {
+            //this.Name = DateTime.Now.ToString("yyyyMMddHH");
+            this.callbacks.Add(eProtocalCommand.CsLogin, On_Login);
+            this.callbacks.Add(eProtocalCommand.CsReportDamage, On_ReportDamage);
+            this.callbacks.Add(eProtocalCommand.CsLeaveRoom, On_Leave);
+            this.callbacks.Add(eProtocalCommand.CsReportUserHp, On_ReportUserHp);
+            var t = Task.Run(MsgLoop);
+            Open();
+        }
+        /// <summary>
+        /// 处理客户端上报玩家剩余血量逻辑
+        /// </summary>
+        /// <param name="peerId"></param>
+        /// <param name="data"></param>
+        void On_ReportUserHp(int peerId, sSocketData data)
+        {
+            var msg = CSReportUserHP.Parser.ParseFrom(data._data);
+            if (this.ClientPeers.TryGetValue(peerId, out var peer))
+            {
+                if (peer.Properties.ContainsKey(PropertyName.Hp))
+                {
+                    peer.Properties[PropertyName.Hp] = msg.UserHP;
+                }
+                else
+                {
+                    peer.Properties.Add(PropertyName.Hp, msg.UserHP);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 处理客户端登录请求
+        /// </summary>
+        void On_Login(int peerId, sSocketData data)
+        {
+            if (this.ClientPeers.TryGetValue(peerId, out var peer))
+            {
+                var msg = CSEnterFight.Parser.ParseFrom(data._data);
+                peer.Properties.Add(nameof(msg.Uid), msg.Uid);
+                peer.Properties.Add(nameof(msg.Zoneid), msg.Zoneid);
+                peer.Properties.Add(PropertyName.Name, msg.Name);
+                lock (Boss_dict)
+                {
+                    if (Boss_dict.TryGetValue(msg.Zoneid, out var boss))
+                    {
+                        if (boss.Ended)                                         // boss战已结束, 尚未重新开启
+                        {
+                            WriteLine("游戏结束1");
+                            peer.SendEvent(eProtocalCommand.ScGameOver, new SCGameOver() { BossHp = boss.Hp, CountDown = boss.CountDownSecs });
+                            peer.Close();
+                        }
+                        else
+                        {
+                            var peerHp = -1;                                    // 玩家血量, -1代表未初始化
+                            var exists = boss.Peers.Where(kv => kv.Value.UID == peer.UID && kv.Value.zoneid == peer.zoneid);
+                            if (exists.Count() < 1)
+                            {
+                                boss.Peers.Add(peerId, peer);
+                            }
+                            else
+                            {                                                   // 已经进入到房间中过了
+                                var oldPeer = exists.First().Value;
+                                if (oldPeer.Properties.TryGetValue(PropertyName.Hp, out var hisHp))
+                                {
+                                    peerHp = int.Parse(hisHp.ToString());
+                                    peer.Properties.Add(PropertyName.Hp, peerHp);
+                                    Console.WriteLine($" 玩家记录血量:{peerHp} ");
+                                }
+                                boss.Peers.Remove(oldPeer.Id);
+                                boss.Peers.Add(peerId, peer);
+                            }
+                            peer.SendEvent(eProtocalCommand.ScLogin, new SCEnterFight() { BossHp = boss.Hp, BossMaxHp = boss.MaxHp, UserHP = peerHp, CountDown = boss.CountDownSecs });
+                            Console.WriteLine($"boss [{boss.Id}] 已经初始化完毕, " + boss.Hp);
+                        }
+                    }
+                    else                                                      // 初始化新的boss
+                    {
+                        Console.WriteLine($"初始化 boss [{msg.Zoneid}]");
+                        var b = new Boss(msg.Zoneid, MaxHp, BossSettle);
+                        b.Peers.Add(peerId, peer);
+                        Boss_dict.Add(msg.Zoneid, b);
+                        peer.SendEvent(eProtocalCommand.ScLogin, new SCEnterFight() { BossHp = b.Hp, BossMaxHp = b.MaxHp, UserHP = -1, CountDown = b.CountDownSecs });
+                    }
+                }
+
+            }
+        }
+        void BossSettle(Boss boss)
+        {
+            WriteLine($"结算Boss {boss.Id}");
+            
+            if (Boss_dict.ContainsKey(boss.Id))
+            {
+                var data = $"zoneid={boss.Id}";
+                HttpHelper.Instance.Post(Config.Ins.SettleUrl, data);
+                var t = Task.Delay(TimeSpan.FromSeconds(Config.Ins.CountDownTimes)).ContinueWith(t =>
+                  {
+                      lock (Boss_dict)
+                      {
+                          Boss_dict.Remove(boss.Id);
+                      }
+                  });
+            }
+        }
+
+        /// <summary>
+        /// 处理客户端上报伤害请求
+        /// </summary>
+        void On_ReportDamage(int peerId, sSocketData data)
+        {
+
+            if (this.ClientPeers.TryGetValue(peerId, out var peer))
+            {
+                var msg = CSFightReportDamage.Parser.ParseFrom(data._data);
+                if (Boss_dict.TryGetValue(peer.zoneid, out var boss))
+                {
+                    boss.DamageQueue.Writer.WriteAsync(msg.Damage);
+                    peer.AddDamage(msg.Damage);
+                }
+            }
+
+        }
+        /// <summary>
+        /// 离开房间(战斗结束)
+        /// </summary>
+        /// <param name="data"></param>
+        void On_Leave(int peerId, sSocketData data)
+        {
+            if (this.ClientPeers.TryGetValue(peerId, out var peer))
+            {
+                peer.Close();
+            }
+        }
+
+        async void MsgLoop()
+        {
+            while (true)
+            {
+                var msg = await MsgChannel.Reader.ReadAsync();
+                if (callbacks.ContainsKey(msg.Value._protocallType))
+                {
+                    callbacks[msg.Value._protocallType](msg.Key, msg.Value);
+                }
+                else
+                {
+                    // 未找到消息处理逻辑
+                }
+            }
+        }
+        /// <summary>
+        /// 开启
+        /// </summary>
+        public void Open()
+        {
+            Boss_dict.Clear();
+            this.CurrentState = RoomState.Open;
+        }
+        public void Close()
+        {
+            this.CurrentState = RoomState.Close;
+            Task.Delay(TimeSpan.FromMinutes(1)).ContinueWith(t => Open());
+        }
+
+
+        private object lock_peers = new object();
+        public void AddPeer(Peer p)
+        {
+            if (CurrentState == RoomState.Open)
+            {
+                lock (lock_peers)
+                {
+                    this.ClientPeers.Add(p.Id, p);
+                }
+            }
+            else
+            {
+               
+                WriteLine("游戏结束2 // 这里不应该执行到!!!");
+                p.SendEvent(eProtocalCommand.ScGameOver, new SCGameOver() { BossHp = 0, CountDown = 9999 });
+                p.Close();
+            }
+        }
+
+        public void RemovePeer(Peer p)
+        {
+            lock (lock_peers)
+            {
+                if (this.ClientPeers.ContainsKey(p.Id))
+                {
+                    this.ClientPeers.Remove(p.Id);
+                }
+            }
+        }
+
+    }
+
+}

+ 86 - 0
CSserver/Lib1/Config.cs

@@ -0,0 +1,86 @@
+using MySql.Data.MySqlClient;
+using StackExchange.Redis;
+
+ 
+   public class Config
+    {
+
+        public const bool GameOnline = true;  
+
+        public ConfigurationOptions redis;
+
+        public MySqlConnectionStringBuilder mysql;
+        /// <summary>
+        /// 结束倒计时
+        /// </summary>
+        public int CountDownTimes => GameOnline ? 10 * 60 : 2 * 60;
+
+        /// <summary>
+        /// 结算地址
+        /// </summary>
+        public string SettleUrl
+        {
+            get
+            {
+                var host = "192.168.10.86";
+                if (GameOnline)
+                {
+                    host = "115.159.121.129";
+                }
+                return $"http://{host}/ylsj2019/Gameserver/Amfphp/service_call/InquireApi/WorldBosSettle.php";
+            }
+        }
+
+        private Config() { }
+
+        static private Config _ins;
+        public static Config Ins
+        {
+            get
+            {
+
+                if (null == _ins)
+                {
+                    if (GameOnline)
+                    {
+                        var host = "127.0.0.1";
+                        var port = 6379;
+                        var pwd = "wanggang1985";
+                        _ins = new Config()
+                        {
+                            redis = ConfigurationOptions.Parse($"{host}:{port},password={pwd},connectTimeout=2000"),
+                            mysql = new MySqlConnectionStringBuilder
+                            {
+                                Server = "127.0.0.1",
+                                UserID = "gwang",
+                                Password = "wanggang1985",
+                                Port = 3306,
+                                Database = "ylsj2019_pay"
+                            }
+                        };
+                    }
+                    else
+                    {
+                        var host = "192.168.10.16";
+                        var port = 6004;
+                        var pwd = "wanggang1985";
+                        _ins = new Config()
+                        {
+                            redis = ConfigurationOptions.Parse($"{host}:{port},password={pwd},connectTimeout=2000"),
+                            mysql = new MySqlConnectionStringBuilder
+                            {
+                                Server = "192.168.10.16",
+                                UserID = "gwang",
+                                Password = "wanggang1985",
+                                Port = 3306,
+                                Database = "ylsj2019_pay"
+                            }
+                        };
+                    }
+                }
+                return _ins;
+            }
+        }
+
+    }
+ 

+ 12 - 0
CSserver/Lib1/Lib1.csproj

@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="MySql.Data" Version="8.0.25" />
+    <PackageReference Include="StackExchange.Redis" Version="2.2.50" />
+  </ItemGroup>
+
+</Project>

+ 112 - 0
CSserver/Lib1/MemKey_User.cs

@@ -0,0 +1,112 @@
+
+public class MemKey_Cfg {
+    /// <summary>
+    /// GM号列表
+    /// </summary>
+    /// <returns></returns>
+    public static readonly string GM_uids_hash = "gamecfg-GM_uids";
+    
+}
+public class MemKey_Game
+{
+    #region"   世界 Boss    "
+
+    /// <summary>
+    /// 世界boss,按照小时分场次
+    /// </summary>
+    /// <param name="zoneid"></param>
+    /// <param name="uid"></param>
+    /// <returns></returns>
+    public static string BossFight_Damage_byDateHour_zset(int zoneid, string dateHour)
+    {
+        return $"gamerun-BossFight-byDateHour-zone{zoneid}-{dateHour}";
+    }
+
+    #endregion
+
+    #region '   公会   '
+    /// <summary>
+    /// 公会
+    /// </summary>
+    /// <param name="zoneid"></param>
+    /// <param name="guildId"></param>
+    /// <returns></returns>
+    public static string Guild(int zoneid,int guildId) => $"guild-{guildId}-zone{zoneid}";
+    #endregion
+}
+
+public class MemKey_User
+{
+
+    #region"    辅助方法  "
+
+    /// <summary>
+    /// 是否使用短名称
+    /// </summary>
+    /// <returns></returns>
+    private static bool bUseShort()
+    {
+        //        return true;  # 全部启用
+        return Config.GameOnline;
+    }
+
+    /// <summary>
+    /// key前缀
+    /// </summary>
+    /// <param name="zoneid"></param>
+    /// <param name="uid"></param>
+    /// <returns></returns>
+    private static string Key_(int zoneid, string uid)
+    {
+        return bUseShort() ? $"u-{uid}-{zoneid}" : $"user-{uid}-zone{zoneid}";
+    }
+
+    #endregion
+
+    #region"    Mail 邮件    "
+
+    /// <summary>
+    /// 玩家邮件 - 当前的maxid
+    /// </summary>
+    /// <param name="zoneid"></param>
+    /// <param name="uid"></param>
+    /// <returns></returns>
+    public static string Mail_CurId_int(int zoneid, string uid)
+    {
+        return Key_(zoneid, uid) + (bUseShort() ? "-m-ci" : "-mail-curid");
+    }
+
+    /// <summary>
+    /// 玩家邮件 - 已领取到的系统邮件id集合
+    /// </summary>
+    /// <param name="zoneid"></param>
+    /// <param name="uid"></param>
+    /// <returns></returns>
+    public static string Mail_SysRecord_set(int zoneid, string uid)
+    {
+        return Key_(zoneid, uid) + (bUseShort() ? "-m-s" : "-mail-sysrecord");
+    }
+
+    /// <summary>
+    /// 玩家邮件 - 邮件列表
+    /// </summary>
+    /// <param name="zoneid"></param>
+    /// <param name="uid"></param>
+    /// <returns></returns>
+    public static string Mail_Queue_hash(int zoneid, string uid)
+    {
+        return Key_(zoneid, uid) + (bUseShort() ? "-m-q" : "-mail-queue");
+    }
+
+    #endregion
+
+    #region '   公会  '
+    /// <summary>
+    /// 玩家公会信息
+    /// </summary>
+    /// <param name="uid"></param>
+    /// <param name="zoneid"></param>
+    /// <returns></returns>
+    public static string Guild(string uid, int zoneid) => $"{Key_(zoneid, uid)}-guild";
+    #endregion
+}

+ 137 - 0
CSserver/Lib1/utils/CompressUtil.cs

@@ -0,0 +1,137 @@
+/*
+ * 文件: CompressUtil.cs
+ * 由SharpDevelop创建。
+ * 作者: gwang
+ * 
+ * 功能: 描述
+ * 版本:
+ *     1.0.0 Created by gwang - 2016/5/14 16:06 
+ */
+
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Text;
+namespace CSharpUtil.Net
+{
+
+    /// <summary>
+    /// 压缩工具类- 注意:由于优化能力有限,本工具限制了待压缩数据的大小不超过1MB.
+    /// </summary>
+    /// <exception cref="DataTooLargeException"></exception>
+    public static class CompressUtil
+    {
+        /// <summary>
+        /// 自定义异常
+        /// </summary>
+        public class DataTooLargeException : Exception
+        {
+            public DataTooLargeException()
+                : base("传入的数据过大(不接受超过1M的数据)!")
+            {
+            }
+        }
+        /// <summary>
+        /// 数据最大长度不宜超过10M
+        /// </summary>
+        const int MaxDataLenght = 1024 * 1024 * 10;
+        /// <summary>
+        /// 利用deflate算法压缩字节数组
+        /// </summary>
+        /// <param name="data">字节数组(不宜超过1M)</param>
+        /// <param name="compressMode">压缩模式(压缩/解压)</param>
+        /// <exception cref="OutOfMemoryException">数据不建议超过1M</exception>
+        /// <returns></returns>
+
+        public static byte[] Deflate(byte[] data, CompressionMode compressMode = CompressionMode.Compress)
+        {
+            if (data.Length > MaxDataLenght)
+            {
+                throw new DataTooLargeException();
+            }
+            else
+            {
+                byte[] buffer = null;
+                using (var outms = new MemoryStream())
+                {
+                    using (var df = new DeflateStream(outms, compressMode, true))
+                    {
+                        df.Write(data, 0, data.Length);
+                    }
+                    outms.Position = 0;
+                    buffer = outms.ToArray();
+                }
+                return buffer;
+            }
+        }
+
+        /// <summary>
+        /// 解压deflate数据
+        /// </summary>
+        /// <param name="data">被压缩的内容</param>
+        /// <exception cref="OutOfMemoryException">数据不建议超过1M</exception>
+        /// <returns></returns>
+        public static byte[] InFlate(byte[] data)
+        {
+            // todo: 这种写法在system.io.compress模式下是支持的.
+            return Deflate(data, CompressionMode.Decompress);
+        }
+
+        /// <summary>
+        /// 解压deflate压缩的字符串数据
+        /// </summary>
+        /// <param name="data"></param>
+        /// <param name="encoder">编码</param>
+        /// <returns>还原到字符串</returns>
+        public static string InFlate(byte[] data, Encoding encoder)
+        {
+            using (var inputStream = new MemoryStream(data))
+            using (var gzip = new DeflateStream(inputStream, CompressionMode.Decompress))
+            using (var reader = new StreamReader(gzip, encoder))
+            {
+                return reader.ReadToEnd();
+            }
+        }
+        /// <summary>
+        /// 解压缩
+        /// </summary>
+        /// <param name="sm">原始文件流</param>
+        /// <returns>还原后的字节数组</returns>
+        public static byte[] InFlate(Stream sm)
+        {
+            using (var outms = new MemoryStream())
+            using (var de = new DeflateStream(sm, CompressionMode.Decompress, true))
+            {
+                var buf = new byte[1024];
+                int len;
+                while ((len = de.Read(buf, 0, buf.Length)) > 0)
+                {
+                    outms.Write(buf, 0, len);
+                }
+
+                return outms.ToArray();
+            }
+        }
+
+        /// <summary>
+        /// string=>utf8_bytes=>deflate=>base64_encode
+        /// </summary>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        public static string zb64encode(string data)
+        {
+            var bytes = Encoding.UTF8.GetBytes(data);
+            return Convert.ToBase64String(Deflate(bytes));
+        }
+        /// <summary>
+        /// string=>base64_decode=>inflate=>utf8_stringfrombytes
+        /// </summary>
+        /// <param name="data"></param>
+        /// <returns></returns>
+        public static string zb64decode(string data)
+        {
+            return Encoding.UTF8.GetString(InFlate(Convert.FromBase64String(data)));
+        }
+    }
+}

+ 55 - 0
CSserver/Lib1/utils/DateTimeExtension.cs

@@ -0,0 +1,55 @@
+/*
+ * 文件: DateTimeExtension.cs
+ * 由SharpDevelop创建。
+ * 作者: gwang
+ * 
+ * 功能: 描述
+ * 版本:
+ *     1.0.0 Created by gwang - 2016/6/25 14:44 
+ */
+using System;
+
+namespace CSharpUtil.Extensions.Time
+{
+
+    /// <summary>
+    /// Description of DateTimeExtension.
+    /// </summary>
+    static public class DateTimeExtension
+    {
+        #region `    扩展DateTime功能,增加Unix时间戳扩展    `
+        /// <summary>
+        /// 从DateTime获取Unix时间戳(按照当前时区换算过了)
+        /// </summary>
+        /// <param name="dt"></param>
+        /// <returns></returns>
+        public static UInt32 ToUnixTimeStamp(this DateTime dt)
+        {
+            return (UInt32)((dt.ToUniversalTime().Ticks - 621355968000000000) / 10000000);
+        }
+        /// <summary>
+        /// Unix时间戳转DateTime(已经自动转换到当前时区了)
+        /// </summary>
+        /// <param name="dt"></param>
+        /// <param name="uiStamp"></param>
+        /// <returns></returns>
+        public static DateTime FromUnixStamp(this DateTime dt, UInt32 uiStamp)
+        {
+            return TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).AddSeconds(uiStamp);
+        }
+
+        /**
+      *  返回自从 Unix 纪元(格林威治时间 1970 年 1 月 1 日 00:00:00)到当前时间的天数。
+      * 检测玩家连续登录及隔天刷新操作用
+      * @param type $time 当前时间戳
+      * @return type
+      */
+        public static int tsDay(this DateTime dt)
+        {
+            return (int)((dt.ToUnixTimeStamp() + 28800) / 86400f); // 东八区前提8小时时差
+        }
+        #endregion
+    }
+
+
+}

+ 180 - 0
CSserver/Lib1/utils/HttpHelper.cs

@@ -0,0 +1,180 @@
+/*
+ * 由SharpDevelop创建。
+ * 用户: gwang
+ * 日期: 2016/5/3
+ * 时间: 17:20
+ * 
+ * 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
+ */
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+//using CSharpUtil.RSA;
+namespace CSharpUtil.Net
+{
+    /// <summary>
+    /// Http辅助类.
+    /// </summary>
+    public class HttpHelper
+    {
+        /// <summary>
+        /// 自定义异常,编码不合约定(Get=>Base64,Post=>deflate)
+        /// </summary>
+        class EncodeException : Exception
+        {
+            public EncodeException()
+                : base("编码")
+            {
+
+            }
+        }
+        /// <summary>
+        /// 自定义的User-Agent
+        /// </summary>
+        const string userAgent = "WebRequest/mail@wanggangzero.cn";
+
+        private static HttpHelper mInst = mInst ?? new HttpHelper();
+        /// <summary>
+        /// 单例
+        /// </summary>
+        public static HttpHelper Instance { get => mInst; }
+
+        private IWebProxy mProxy = new WebProxy();
+        /// <summary>
+        /// 可以指定代理服务器
+        /// </summary>
+        public IWebProxy Proxy { get => mProxy; set => mProxy = value; }
+
+        public string Post(string url, string data) => mWebRequest(url, data, "POST", Encoding.UTF8);
+
+        private HttpWebRequest mGetWebRequest(string url, string method)
+        {
+            var webReq = (HttpWebRequest)WebRequest.Create(new Uri(url));
+            webReq.Method = method;
+            webReq.KeepAlive = true;
+            webReq.UserAgent = userAgent;
+            webReq.Proxy = mProxy;
+            return webReq;
+        }
+        private string mWebRequest(string url, string data, string method, Encoding encode)
+        {
+            var ret = string.Empty;
+            var getUrl = (method.ToUpper() == "GET") ? url + "?" + data : url;
+            var webReq = mGetWebRequest(getUrl, method);
+            if (method.ToUpper() == "POST")
+            {       // 写post流
+                webReq.ServicePoint.Expect100Continue = false;   // Post优化,去掉问答流程,提速,避免一些服务器不支持.		
+                try
+                {
+                    using (var nstream = webReq.GetRequestStream())
+                    {
+                        var byteArray = encode.GetBytes(data);
+                        nstream.Write(byteArray, 0, byteArray.Length);
+                    }
+                }
+                catch (Exception e)
+                {
+                    return "net fail";
+                }
+            }
+            // 读取返回值
+            using (var response = (HttpWebResponse)webReq.GetResponse())
+            using (var sr = new StreamReader(response.GetResponseStream(), encode))
+            {
+                if (response.StatusCode != HttpStatusCode.OK)
+                {
+                    return response.StatusDescription;
+                }
+                ret = sr.ReadToEnd();
+            }
+
+            return ret;
+        }
+
+        #region ` 自定义传输模式 post=>defalte,get=>base64  `
+        /// <summary>
+        /// 发送deflate
+        /// </summary>
+        /// <param name="url"></param>
+        /// <param name="data"></param>
+        /// <param name="encode"></param>
+        /// <returns></returns>
+        public string PostDeflate(string url, string data, Encoding encode) => MyWebRequest(url, data, "POST", encode);
+
+        public string GetBase64(string url, string data, Encoding encode) => MyWebRequest(url, data, "GET", encode);
+
+        private string MyWebRequest(string url, string data, string method, Encoding encode)
+        {
+            var ret = string.Empty;
+
+            var getUrl = (method.ToUpper() == "GET") ? url + "?" + Convert.ToBase64String(encode.GetBytes(data)) : url;
+
+            var webReq = (HttpWebRequest)WebRequest.Create(new Uri(getUrl));
+            webReq.Method = method;
+            webReq.KeepAlive = true;
+            webReq.UserAgent = userAgent;
+            webReq.Proxy = mProxy;
+            if (method.ToUpper() == "POST")
+            {       // 写post流
+                webReq.ContentType = "application/octet-stream";  // 注意这里不使用deflate以免中间环节意外解压
+                webReq.ServicePoint.Expect100Continue = false;   // Post优化,去掉问答流程,提速,避免一些服务器不支持.		
+                using (Stream nstream = webReq.GetRequestStream())
+                {
+                    var byteArray = CompressUtil.Deflate(encode.GetBytes(data));
+                    nstream.Write(byteArray, 0, byteArray.Length);
+                }
+            }
+            // 读取返回值
+            using (var response = (HttpWebResponse)webReq.GetResponse())
+            {
+                if (method.ToUpper() == "GET")
+                {
+                    using (var sr = new StreamReader(response.GetResponseStream(), encode))
+                    {
+                        ret = encode.GetString(Convert.FromBase64String(sr.ReadToEnd()));
+                    }
+                }
+                else
+                {
+                    ret = encode.GetString(CompressUtil.InFlate(response.GetResponseStream()));
+                }
+            }
+
+            return ret;
+        }
+
+        #endregion
+
+
+        #region `   签名   `
+        ///// <summary>
+        ///// http参数签名
+        ///// </summary>
+        ///// <param name="dic"></param>
+        ///// <param name="privKeyFileName"></param>
+        ///// <returns></returns>
+        //public static string Sign(IDictionary<string, string> dic, string privKeyFileName)
+        //{
+        //    var queryStr = getQueryString(dic);
+
+        //    return RSAUtil.RSASign(queryStr, privKeyFileName);
+
+        //}
+        /// <summary>
+        /// 将参数数组组装成URLQuerystring.
+        /// </summary>
+        /// <param name="parameters"></param>
+        /// <param name="sorted"></param>
+        /// <returns></returns>
+        public static string getQueryString(IDictionary<string, string> parameters, bool sorted = true)
+        {
+            return string.Join("&", (from kv in (sorted ? parameters.OrderBy(kv => kv.Key) : parameters.AsEnumerable()) select kv.Key + "=" + kv.Value).ToArray());
+        }
+
+        #endregion
+    }
+
+}

+ 53 - 0
CSserver/Lib1/utils/Singleton.cs

@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CSharpUtil
+{
+    /// <summary>
+    /// 普通单例
+    /// </summary>
+    /// <typeparam name="T"></typeparam>
+    public class Singleton<T> where T : Singleton<T>, new()
+    {
+        /// <summary>
+        /// 单例
+        /// </summary>
+        private static T m_Instance;
+
+        /// <summary>
+        /// 实例
+        /// </summary>
+        public static T Instance
+        {
+            get
+            {
+                if (object.ReferenceEquals(Singleton<T>.m_Instance, null))
+                {
+                    var obj = new T();
+                    Singleton<T>.m_Instance = obj;
+                    Container.Add(typeof(T).Name, obj);
+                }
+                return Singleton<T>.m_Instance;
+            }
+        }
+
+
+        #region '   内部容器  ' 
+        static Dictionary<string, Object> _mC;
+        static Dictionary<string, Object> Container
+        {
+            get
+            {
+                if (null == _mC)
+                {
+                    _mC = new Dictionary<string, object>();
+                }
+                return _mC;
+            }
+        }
+        #endregion
+    }
+}

+ 26 - 0
CSserver/Lib1/utils/SocketUtils.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Runtime.InteropServices;
+using System.Net.Sockets;
+
+namespace CSharpUtil.Extensions.Net
+{
+    /// <summary>
+    /// 辅助类
+    /// </summary>
+    public static  class SocketUtils
+    {
+        public static void SetKeepAlive(this Socket sock, int interval_ms)
+        {
+            uint dummy = 0;
+            byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
+            BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
+            BitConverter.GetBytes((uint)interval_ms).CopyTo(inOptionValues, Marshal.SizeOf(dummy));
+            BitConverter.GetBytes((uint)interval_ms).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);
+            sock.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
+        }
+    }
+}

+ 40 - 0
CSserver/Lib1/utils/TaskWaitingExtension.cs

@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace CSharpUtil.Extensions.Tasks
+{
+ 
+    public static class TaskWaitingExtensions
+    {
+        public static async Task<TResult> WaitAsync<TResult>(this Task<TResult> task, TimeSpan timeout)
+        {
+            using (var timeoutCancellationTokenSource = new CancellationTokenSource())
+            {
+                var delayTask = Task.Delay(timeout, timeoutCancellationTokenSource.Token);
+                if (await Task.WhenAny(task, delayTask) == task)
+                {
+                    timeoutCancellationTokenSource.Cancel();
+                    return await task;
+                }
+                throw new TimeoutException("The operation has timed out.");
+            }
+        }
+        public static async Task WaitAsync(this Task task, TimeSpan timeout)
+        {
+            using (var timeoutCancellationTokenSource = new CancellationTokenSource())
+            {
+                var delayTask = Task.Delay(timeout, timeoutCancellationTokenSource.Token);
+                if (await Task.WhenAny(task, delayTask) == task)
+                {
+                    timeoutCancellationTokenSource.Cancel();
+                    return;
+                }
+                throw new TimeoutException("The operation has timed out.");
+            }
+        }
+    }
+}

+ 170 - 0
CSserver/PBReferens/DataBuff.cs

@@ -0,0 +1,170 @@
+using System;
+
+namespace ProtoDataBuff
+{
+
+    //常量数据
+    public class Constants
+    {
+        //消息:数据总长度(4byte) + 数据类型(2byte) + 数据(N byte)
+        public static int HEAD_DATA_LEN = 4;
+        public static int HEAD_TYPE_LEN = 2;
+        public static int HEAD_LEN//6byte
+        {
+            get { return HEAD_DATA_LEN + HEAD_TYPE_LEN; }
+        }
+    }
+
+    /// <summary>
+    /// 网络数据结构
+    /// </summary>
+    [Serializable]
+    public struct sSocketData
+    {
+        public byte[] _data;
+        public eProtocalCommand _protocallType;
+        public int _buffLength;
+        public int _dataLength;
+        /// <summary>
+        /// 网络结构转数据
+        /// </summary>
+        /// <param name="tmpSocketData"></param>
+        /// <returns></returns>
+        public byte[] ToBytes()
+        {
+            byte[] _tmpBuff = new byte[_buffLength];
+            byte[] _tmpBuffLength = BitConverter.GetBytes(_buffLength);
+            byte[] _tmpDataLenght = BitConverter.GetBytes((UInt16)_protocallType);
+
+            Array.Copy(_tmpBuffLength, 0, _tmpBuff, 0, Constants.HEAD_DATA_LEN);//缓存总长度
+            Array.Copy(_tmpDataLenght, 0, _tmpBuff, Constants.HEAD_DATA_LEN, Constants.HEAD_TYPE_LEN);//协议类型
+            Array.Copy(_data, 0, _tmpBuff, Constants.HEAD_LEN, _dataLength);//协议数据
+
+            return _tmpBuff;
+        }
+
+        /// <summary>
+        /// 数据转网络结构
+        /// </summary>
+        /// <param name="_protocalType"></param>
+        /// <param name="_data"></param>
+        /// <returns></returns>
+        public static sSocketData FromBytes(eProtocalCommand _protocalType, byte[] _data)
+        {
+            sSocketData tmpSocketData = new();
+            tmpSocketData._buffLength = Constants.HEAD_LEN + _data.Length;
+            tmpSocketData._dataLength = _data.Length;
+            tmpSocketData._protocallType = _protocalType;
+            tmpSocketData._data = _data;
+            return tmpSocketData;
+        }
+    }
+
+    /// <summary>
+    /// 网络数据缓存器,//自动大小数据缓存器
+    /// </summary>
+    [Serializable]
+    public class DataBuffer
+    {
+        private int _minBuffLen;
+        private byte[] _buff;
+        private int _curBuffPosition;
+        private int _buffLength = 0;
+        private int _dataLength;
+        private UInt16 _protocalType;
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="_minBuffLen">最小缓冲区大小</param>
+        public DataBuffer(int _minBuffLen = 1024)
+        {
+            if (_minBuffLen <= 0)
+            {
+                this._minBuffLen = 1024;
+            }
+            else
+            {
+                this._minBuffLen = _minBuffLen;
+            }
+            _buff = new byte[this._minBuffLen];
+        }
+
+        /// <summary>
+        /// 添加缓存数据
+        /// </summary>
+        /// <param name="_data"></param>
+        /// <param name="_dataLen"></param>
+        public void AddBuffer(byte[] _data, int _dataLen)
+        {
+            if (_dataLen > _buff.Length - _curBuffPosition)//超过当前缓存
+            {
+                byte[] _tmpBuff = new byte[_curBuffPosition + _dataLen];
+                Array.Copy(_buff, 0, _tmpBuff, 0, _curBuffPosition);
+                Array.Copy(_data, 0, _tmpBuff, _curBuffPosition, _dataLen);
+                _buff = _tmpBuff;
+                _tmpBuff = null;
+            }
+            else
+            {
+                Array.Copy(_data, 0, _buff, _curBuffPosition, _dataLen);
+            }
+            _curBuffPosition += _dataLen;//修改当前数据标记
+        }
+
+        /// <summary>
+        /// 更新数据长度
+        /// </summary>
+        public void UpdateDataLength()
+        {
+            if (_dataLength == 0 && _curBuffPosition >= Constants.HEAD_LEN)
+            {
+                byte[] tmpDataLen = new byte[Constants.HEAD_DATA_LEN];
+                Array.Copy(_buff, 0, tmpDataLen, 0, Constants.HEAD_DATA_LEN);
+                _buffLength = BitConverter.ToInt32(tmpDataLen, 0);
+
+                byte[] tmpProtocalType = new byte[Constants.HEAD_TYPE_LEN];
+                Array.Copy(_buff, Constants.HEAD_DATA_LEN, tmpProtocalType, 0, Constants.HEAD_TYPE_LEN);
+                _protocalType = BitConverter.ToUInt16(tmpProtocalType, 0);
+
+                _dataLength = _buffLength - Constants.HEAD_LEN;
+            }
+        }
+
+        /// <summary>
+        /// 获取一条可用数据,返回值标记是否有数据
+        /// </summary>
+        /// <param name="_tmpSocketData"></param>
+        /// <returns></returns>
+        public bool GetData(out sSocketData _tmpSocketData)
+        {
+            _tmpSocketData = new sSocketData();
+
+            if (_buffLength <= 0)
+            {
+                UpdateDataLength();
+            }
+
+            if (_buffLength > 0 && _curBuffPosition >= _buffLength)
+            {
+                _tmpSocketData._buffLength = _buffLength;
+                _tmpSocketData._dataLength = _dataLength;
+                _tmpSocketData._protocallType = (eProtocalCommand)_protocalType;
+                _tmpSocketData._data = new byte[_dataLength];
+                Array.Copy(_buff, Constants.HEAD_LEN, _tmpSocketData._data, 0, _dataLength);
+                _curBuffPosition -= _buffLength;
+                byte[] _tmpBuff = new byte[_curBuffPosition < _minBuffLen ? _minBuffLen : _curBuffPosition];
+                Array.Copy(_buff, _buffLength, _tmpBuff, 0, _curBuffPosition);
+                _buff = _tmpBuff;
+
+
+                _buffLength = 0;
+                _dataLength = 0;
+                _protocalType = 0;
+                return true;
+            }
+            return false;
+        }
+
+    }
+}

+ 16 - 0
CSserver/PBReferens/PBReferens.csproj

@@ -0,0 +1,16 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+  </PropertyGroup>
+
+  <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
+    <Exec Command="protoc --csharp_out=pbcs pb/*.proto" />
+  </Target>
+
+  <ItemGroup>
+    <PackageReference Include="Google.Protobuf" Version="3.17.3" />
+    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
+  </ItemGroup>
+
+</Project>

+ 8 - 0
CSserver/PBReferens/pb/ErrCode.proto

@@ -0,0 +1,8 @@
+syntax = "proto3";
+
+ // package BossServer; //暂时没有定 命名空间
+
+enum ErrCode {
+  ok = 0;
+  OpNotAllowed = 1;
+}

+ 33 - 0
CSserver/PBReferens/pb/MsgTypeEnum.proto

@@ -0,0 +1,33 @@
+syntax = "proto3";
+
+ // package BossServer; //暂时没有定 命名空间
+
+enum eProtocalCommand {
+  // 无操作
+  noop = 0;
+  // 登录()
+  CS_Login = 1;  
+  // 上报伤害输出
+  CS_ReportDamage =2;
+  // 离开房间(战斗失败结束)
+  CS_LeaveRoom =3;
+  // 上报玩家剩余血量
+  CS_ReportUserHp=4;
+  // 聊天, 客户端初始化
+  CS_ChatLogin = 10;
+  // 聊天, 发送消息
+  CS_ChatSendMsg = 11;
+
+  // 登录结果
+  SC_Login =100;
+  // 更新数据
+  SC_UpdateProperties =101;
+  // 游戏结束
+  SC_GameOver=102;
+
+  // 聊天, 登录成功
+  SC_ChatLogin = 110;
+  // 聊天, 新消息
+  SC_ChatNewMsg = 111;
+
+}

+ 58 - 0
CSserver/PBReferens/pb/chat.proto

@@ -0,0 +1,58 @@
+syntax = "proto3";
+
+package Chat;
+ 
+
+// 频道
+enum ChatChannel {
+	// 系统频道
+	System = 0;
+	// 世界频道
+	World = 1;
+	// 公会频道
+	Guild =2;
+}
+
+// 聊天, 登录
+message CS_ChatLogin
+{
+	int32 Zoneid = 1;
+	string Uid = 2;
+	string Name =3;
+}
+
+// 聊天, 发送消息
+message CS_ChatSendMsg
+{
+  
+	ChatChannel ToChannel = 1;
+	string  Msg = 2;
+
+}
+
+// 聊天, 初始化返回值
+message SC_ChatLogin
+{
+     // 错误码
+	enum ErrorCode
+	{
+	     // 成功
+		 OK = 0;
+		 // 未知的UID
+		 AccIDInvalid = 1;
+		 // 重复登入
+		 LoginRepeat = 2;
+		 // 内部错误
+		 InnerError = 3;
+	}
+	// 错误码
+	ErrorCode code = 1;
+} 
+
+// 聊天, 新的消息
+message SC_ChatNewMsg
+{ 
+	ChatChannel FromChannel = 1;
+	string Msg = 2; 
+	string SenderName = 3;
+}

+ 55 - 0
CSserver/PBReferens/pb/fight.proto

@@ -0,0 +1,55 @@
+//import "google/protobuf/any.proto";
+syntax = "proto3";
+
+package BossServer;
+ 
+
+//上报伤害输出
+message CSFightReportDamage
+{
+       // 伤害输出
+	  int32 Damage = 1;
+}
+// 上报玩家剩余血量
+message CSReportUserHP{
+	int32 UserHP = 1;
+}
+// 结束(一般情况下是玩家血量到底主动退出)
+message CSLeaveFight
+{
+	  int32 _no =1;
+}
+ 
+// 从连接状态转入已经登陆状态(标记自己的uid和zoneid)
+message CSEnterFight
+{
+	int32 Zoneid = 1;
+	string Uid = 2;
+	string Name =3;
+
+}
+
+// 登陆完成
+message SCEnterFight
+{
+	int32 BossHp = 1;
+	int32 BossMaxHp = 2;
+	int32 UserHP = 3;
+	// 倒计时
+	int32 CountDown=4;
+	
+}
+// 更新状态数据
+message SCUpdateProperties
+{
+   int32 BossHp=1;
+   int32 BossMaxHp=2;
+   // 倒计时
+	int32 CountDown=3;
+}
+// 战斗结束
+message SCGameOver{
+	int32 BossHp=1;	
+	// 倒计时
+	int32 CountDown=2;
+}

+ 725 - 0
CSserver/PBReferens/pbcs/Chat.cs

@@ -0,0 +1,725 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: pb/chat.proto
+#pragma warning disable 1591, 0612, 3021
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace Chat {
+
+  /// <summary>Holder for reflection information generated from pb/chat.proto</summary>
+  public static partial class ChatReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for pb/chat.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static ChatReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "Cg1wYi9jaGF0LnByb3RvEgRDaGF0IjkKDENTX0NoYXRMb2dpbhIOCgZab25l",
+            "aWQYASABKAUSCwoDVWlkGAIgASgJEgwKBE5hbWUYAyABKAkiQwoOQ1NfQ2hh",
+            "dFNlbmRNc2cSJAoJVG9DaGFubmVsGAEgASgOMhEuQ2hhdC5DaGF0Q2hhbm5l",
+            "bBILCgNNc2cYAiABKAkiggEKDFNDX0NoYXRMb2dpbhIqCgRjb2RlGAEgASgO",
+            "MhwuQ2hhdC5TQ19DaGF0TG9naW4uRXJyb3JDb2RlIkYKCUVycm9yQ29kZRIG",
+            "CgJPSxAAEhAKDEFjY0lESW52YWxpZBABEg8KC0xvZ2luUmVwZWF0EAISDgoK",
+            "SW5uZXJFcnJvchADIlgKDVNDX0NoYXROZXdNc2cSJgoLRnJvbUNoYW5uZWwY",
+            "ASABKA4yES5DaGF0LkNoYXRDaGFubmVsEgsKA01zZxgCIAEoCRISCgpTZW5k",
+            "ZXJOYW1lGAMgASgJKi8KC0NoYXRDaGFubmVsEgoKBlN5c3RlbRAAEgkKBVdv",
+            "cmxkEAESCQoFR3VpbGQQAmIGcHJvdG8z"));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { },
+          new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Chat.ChatChannel), }, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chat.CS_ChatLogin), global::Chat.CS_ChatLogin.Parser, new[]{ "Zoneid", "Uid", "Name" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chat.CS_ChatSendMsg), global::Chat.CS_ChatSendMsg.Parser, new[]{ "ToChannel", "Msg" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chat.SC_ChatLogin), global::Chat.SC_ChatLogin.Parser, new[]{ "Code" }, null, new[]{ typeof(global::Chat.SC_ChatLogin.Types.ErrorCode) }, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::Chat.SC_ChatNewMsg), global::Chat.SC_ChatNewMsg.Parser, new[]{ "FromChannel", "Msg", "SenderName" }, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Enums
+  /// <summary>
+  /// 频道
+  /// </summary>
+  public enum ChatChannel {
+    /// <summary>
+    /// 系统频道
+    /// </summary>
+    [pbr::OriginalName("System")] System = 0,
+    /// <summary>
+    /// 世界频道
+    /// </summary>
+    [pbr::OriginalName("World")] World = 1,
+    /// <summary>
+    /// 公会频道
+    /// </summary>
+    [pbr::OriginalName("Guild")] Guild = 2,
+  }
+
+  #endregion
+
+  #region Messages
+  /// <summary>
+  /// 聊天, 登录
+  /// </summary>
+  public sealed partial class CS_ChatLogin : pb::IMessage<CS_ChatLogin> {
+    private static readonly pb::MessageParser<CS_ChatLogin> _parser = new pb::MessageParser<CS_ChatLogin>(() => new CS_ChatLogin());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<CS_ChatLogin> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chat.ChatReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CS_ChatLogin() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CS_ChatLogin(CS_ChatLogin other) : this() {
+      zoneid_ = other.zoneid_;
+      uid_ = other.uid_;
+      name_ = other.name_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CS_ChatLogin Clone() {
+      return new CS_ChatLogin(this);
+    }
+
+    /// <summary>Field number for the "Zoneid" field.</summary>
+    public const int ZoneidFieldNumber = 1;
+    private int zoneid_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int Zoneid {
+      get { return zoneid_; }
+      set {
+        zoneid_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "Uid" field.</summary>
+    public const int UidFieldNumber = 2;
+    private string uid_ = "";
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string Uid {
+      get { return uid_; }
+      set {
+        uid_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "Name" field.</summary>
+    public const int NameFieldNumber = 3;
+    private string name_ = "";
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as CS_ChatLogin);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(CS_ChatLogin other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Zoneid != other.Zoneid) return false;
+      if (Uid != other.Uid) return false;
+      if (Name != other.Name) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Zoneid != 0) hash ^= Zoneid.GetHashCode();
+      if (Uid.Length != 0) hash ^= Uid.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (Zoneid != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(Zoneid);
+      }
+      if (Uid.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Uid);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Name);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (Zoneid != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Zoneid);
+      }
+      if (Uid.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Uid);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(CS_ChatLogin other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Zoneid != 0) {
+        Zoneid = other.Zoneid;
+      }
+      if (other.Uid.Length != 0) {
+        Uid = other.Uid;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            Zoneid = input.ReadInt32();
+            break;
+          }
+          case 18: {
+            Uid = input.ReadString();
+            break;
+          }
+          case 26: {
+            Name = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  /// 聊天, 发送消息
+  /// </summary>
+  public sealed partial class CS_ChatSendMsg : pb::IMessage<CS_ChatSendMsg> {
+    private static readonly pb::MessageParser<CS_ChatSendMsg> _parser = new pb::MessageParser<CS_ChatSendMsg>(() => new CS_ChatSendMsg());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<CS_ChatSendMsg> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chat.ChatReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CS_ChatSendMsg() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CS_ChatSendMsg(CS_ChatSendMsg other) : this() {
+      toChannel_ = other.toChannel_;
+      msg_ = other.msg_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CS_ChatSendMsg Clone() {
+      return new CS_ChatSendMsg(this);
+    }
+
+    /// <summary>Field number for the "ToChannel" field.</summary>
+    public const int ToChannelFieldNumber = 1;
+    private global::Chat.ChatChannel toChannel_ = 0;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public global::Chat.ChatChannel ToChannel {
+      get { return toChannel_; }
+      set {
+        toChannel_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "Msg" field.</summary>
+    public const int MsgFieldNumber = 2;
+    private string msg_ = "";
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string Msg {
+      get { return msg_; }
+      set {
+        msg_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as CS_ChatSendMsg);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(CS_ChatSendMsg other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (ToChannel != other.ToChannel) return false;
+      if (Msg != other.Msg) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (ToChannel != 0) hash ^= ToChannel.GetHashCode();
+      if (Msg.Length != 0) hash ^= Msg.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (ToChannel != 0) {
+        output.WriteRawTag(8);
+        output.WriteEnum((int) ToChannel);
+      }
+      if (Msg.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Msg);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (ToChannel != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) ToChannel);
+      }
+      if (Msg.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Msg);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(CS_ChatSendMsg other) {
+      if (other == null) {
+        return;
+      }
+      if (other.ToChannel != 0) {
+        ToChannel = other.ToChannel;
+      }
+      if (other.Msg.Length != 0) {
+        Msg = other.Msg;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            toChannel_ = (global::Chat.ChatChannel) input.ReadEnum();
+            break;
+          }
+          case 18: {
+            Msg = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  /// 聊天, 初始化返回值
+  /// </summary>
+  public sealed partial class SC_ChatLogin : pb::IMessage<SC_ChatLogin> {
+    private static readonly pb::MessageParser<SC_ChatLogin> _parser = new pb::MessageParser<SC_ChatLogin>(() => new SC_ChatLogin());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<SC_ChatLogin> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chat.ChatReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SC_ChatLogin() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SC_ChatLogin(SC_ChatLogin other) : this() {
+      code_ = other.code_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SC_ChatLogin Clone() {
+      return new SC_ChatLogin(this);
+    }
+
+    /// <summary>Field number for the "code" field.</summary>
+    public const int CodeFieldNumber = 1;
+    private global::Chat.SC_ChatLogin.Types.ErrorCode code_ = 0;
+    /// <summary>
+    /// 错误码
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public global::Chat.SC_ChatLogin.Types.ErrorCode Code {
+      get { return code_; }
+      set {
+        code_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as SC_ChatLogin);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(SC_ChatLogin other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Code != other.Code) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Code != 0) hash ^= Code.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (Code != 0) {
+        output.WriteRawTag(8);
+        output.WriteEnum((int) Code);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (Code != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Code);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(SC_ChatLogin other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Code != 0) {
+        Code = other.Code;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            code_ = (global::Chat.SC_ChatLogin.Types.ErrorCode) input.ReadEnum();
+            break;
+          }
+        }
+      }
+    }
+
+    #region Nested types
+    /// <summary>Container for nested types declared in the SC_ChatLogin message type.</summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static partial class Types {
+      /// <summary>
+      /// 错误码
+      /// </summary>
+      public enum ErrorCode {
+        /// <summary>
+        /// 成功
+        /// </summary>
+        [pbr::OriginalName("OK")] Ok = 0,
+        /// <summary>
+        /// 未知的UID
+        /// </summary>
+        [pbr::OriginalName("AccIDInvalid")] AccIdinvalid = 1,
+        /// <summary>
+        /// 重复登入
+        /// </summary>
+        [pbr::OriginalName("LoginRepeat")] LoginRepeat = 2,
+        /// <summary>
+        /// 内部错误
+        /// </summary>
+        [pbr::OriginalName("InnerError")] InnerError = 3,
+      }
+
+    }
+    #endregion
+
+  }
+
+  /// <summary>
+  /// 聊天, 新的消息
+  /// </summary>
+  public sealed partial class SC_ChatNewMsg : pb::IMessage<SC_ChatNewMsg> {
+    private static readonly pb::MessageParser<SC_ChatNewMsg> _parser = new pb::MessageParser<SC_ChatNewMsg>(() => new SC_ChatNewMsg());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<SC_ChatNewMsg> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::Chat.ChatReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SC_ChatNewMsg() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SC_ChatNewMsg(SC_ChatNewMsg other) : this() {
+      fromChannel_ = other.fromChannel_;
+      msg_ = other.msg_;
+      senderName_ = other.senderName_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SC_ChatNewMsg Clone() {
+      return new SC_ChatNewMsg(this);
+    }
+
+    /// <summary>Field number for the "FromChannel" field.</summary>
+    public const int FromChannelFieldNumber = 1;
+    private global::Chat.ChatChannel fromChannel_ = 0;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public global::Chat.ChatChannel FromChannel {
+      get { return fromChannel_; }
+      set {
+        fromChannel_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "Msg" field.</summary>
+    public const int MsgFieldNumber = 2;
+    private string msg_ = "";
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string Msg {
+      get { return msg_; }
+      set {
+        msg_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "SenderName" field.</summary>
+    public const int SenderNameFieldNumber = 3;
+    private string senderName_ = "";
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string SenderName {
+      get { return senderName_; }
+      set {
+        senderName_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as SC_ChatNewMsg);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(SC_ChatNewMsg other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (FromChannel != other.FromChannel) return false;
+      if (Msg != other.Msg) return false;
+      if (SenderName != other.SenderName) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (FromChannel != 0) hash ^= FromChannel.GetHashCode();
+      if (Msg.Length != 0) hash ^= Msg.GetHashCode();
+      if (SenderName.Length != 0) hash ^= SenderName.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (FromChannel != 0) {
+        output.WriteRawTag(8);
+        output.WriteEnum((int) FromChannel);
+      }
+      if (Msg.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Msg);
+      }
+      if (SenderName.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(SenderName);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (FromChannel != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) FromChannel);
+      }
+      if (Msg.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Msg);
+      }
+      if (SenderName.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(SenderName);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(SC_ChatNewMsg other) {
+      if (other == null) {
+        return;
+      }
+      if (other.FromChannel != 0) {
+        FromChannel = other.FromChannel;
+      }
+      if (other.Msg.Length != 0) {
+        Msg = other.Msg;
+      }
+      if (other.SenderName.Length != 0) {
+        SenderName = other.SenderName;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            fromChannel_ = (global::Chat.ChatChannel) input.ReadEnum();
+            break;
+          }
+          case 18: {
+            Msg = input.ReadString();
+            break;
+          }
+          case 26: {
+            SenderName = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code

+ 41 - 0
CSserver/PBReferens/pbcs/ErrCode.cs

@@ -0,0 +1,41 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: pb/ErrCode.proto
+#pragma warning disable 1591, 0612, 3021
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+/// <summary>Holder for reflection information generated from pb/ErrCode.proto</summary>
+public static partial class ErrCodeReflection {
+
+  #region Descriptor
+  /// <summary>File descriptor for pb/ErrCode.proto</summary>
+  public static pbr::FileDescriptor Descriptor {
+    get { return descriptor; }
+  }
+  private static pbr::FileDescriptor descriptor;
+
+  static ErrCodeReflection() {
+    byte[] descriptorData = global::System.Convert.FromBase64String(
+        string.Concat(
+          "ChBwYi9FcnJDb2RlLnByb3RvKiMKB0VyckNvZGUSBgoCb2sQABIQCgxPcE5v",
+          "dEFsbG93ZWQQAWIGcHJvdG8z"));
+    descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+        new pbr::FileDescriptor[] { },
+        new pbr::GeneratedClrTypeInfo(new[] {typeof(global::ErrCode), }, null));
+  }
+  #endregion
+
+}
+#region Enums
+public enum ErrCode {
+  [pbr::OriginalName("ok")] Ok = 0,
+  [pbr::OriginalName("OpNotAllowed")] OpNotAllowed = 1,
+}
+
+#endregion
+
+
+#endregion Designer generated code

+ 1131 - 0
CSserver/PBReferens/pbcs/Fight.cs

@@ -0,0 +1,1131 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: pb/fight.proto
+#pragma warning disable 1591, 0612, 3021
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+namespace BossServer {
+
+  /// <summary>Holder for reflection information generated from pb/fight.proto</summary>
+  public static partial class FightReflection {
+
+    #region Descriptor
+    /// <summary>File descriptor for pb/fight.proto</summary>
+    public static pbr::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static pbr::FileDescriptor descriptor;
+
+    static FightReflection() {
+      byte[] descriptorData = global::System.Convert.FromBase64String(
+          string.Concat(
+            "Cg5wYi9maWdodC5wcm90bxIKQm9zc1NlcnZlciIlChNDU0ZpZ2h0UmVwb3J0",
+            "RGFtYWdlEg4KBkRhbWFnZRgBIAEoBSIgCg5DU1JlcG9ydFVzZXJIUBIOCgZV",
+            "c2VySFAYASABKAUiGwoMQ1NMZWF2ZUZpZ2h0EgsKA19ubxgBIAEoBSI5CgxD",
+            "U0VudGVyRmlnaHQSDgoGWm9uZWlkGAEgASgFEgsKA1VpZBgCIAEoCRIMCgRO",
+            "YW1lGAMgASgJIlQKDFNDRW50ZXJGaWdodBIOCgZCb3NzSHAYASABKAUSEQoJ",
+            "Qm9zc01heEhwGAIgASgFEg4KBlVzZXJIUBgDIAEoBRIRCglDb3VudERvd24Y",
+            "BCABKAUiSgoSU0NVcGRhdGVQcm9wZXJ0aWVzEg4KBkJvc3NIcBgBIAEoBRIR",
+            "CglCb3NzTWF4SHAYAiABKAUSEQoJQ291bnREb3duGAMgASgFIi8KClNDR2Ft",
+            "ZU92ZXISDgoGQm9zc0hwGAEgASgFEhEKCUNvdW50RG93bhgCIAEoBWIGcHJv",
+            "dG8z"));
+      descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+          new pbr::FileDescriptor[] { },
+          new pbr::GeneratedClrTypeInfo(null, new pbr::GeneratedClrTypeInfo[] {
+            new pbr::GeneratedClrTypeInfo(typeof(global::BossServer.CSFightReportDamage), global::BossServer.CSFightReportDamage.Parser, new[]{ "Damage" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::BossServer.CSReportUserHP), global::BossServer.CSReportUserHP.Parser, new[]{ "UserHP" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::BossServer.CSLeaveFight), global::BossServer.CSLeaveFight.Parser, new[]{ "No" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::BossServer.CSEnterFight), global::BossServer.CSEnterFight.Parser, new[]{ "Zoneid", "Uid", "Name" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::BossServer.SCEnterFight), global::BossServer.SCEnterFight.Parser, new[]{ "BossHp", "BossMaxHp", "UserHP", "CountDown" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::BossServer.SCUpdateProperties), global::BossServer.SCUpdateProperties.Parser, new[]{ "BossHp", "BossMaxHp", "CountDown" }, null, null, null),
+            new pbr::GeneratedClrTypeInfo(typeof(global::BossServer.SCGameOver), global::BossServer.SCGameOver.Parser, new[]{ "BossHp", "CountDown" }, null, null, null)
+          }));
+    }
+    #endregion
+
+  }
+  #region Messages
+  /// <summary>
+  ///上报伤害输出
+  /// </summary>
+  public sealed partial class CSFightReportDamage : pb::IMessage<CSFightReportDamage> {
+    private static readonly pb::MessageParser<CSFightReportDamage> _parser = new pb::MessageParser<CSFightReportDamage>(() => new CSFightReportDamage());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<CSFightReportDamage> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::BossServer.FightReflection.Descriptor.MessageTypes[0]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CSFightReportDamage() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CSFightReportDamage(CSFightReportDamage other) : this() {
+      damage_ = other.damage_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CSFightReportDamage Clone() {
+      return new CSFightReportDamage(this);
+    }
+
+    /// <summary>Field number for the "Damage" field.</summary>
+    public const int DamageFieldNumber = 1;
+    private int damage_;
+    /// <summary>
+    /// 伤害输出
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int Damage {
+      get { return damage_; }
+      set {
+        damage_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as CSFightReportDamage);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(CSFightReportDamage other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Damage != other.Damage) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Damage != 0) hash ^= Damage.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (Damage != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(Damage);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (Damage != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Damage);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(CSFightReportDamage other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Damage != 0) {
+        Damage = other.Damage;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            Damage = input.ReadInt32();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  /// 上报玩家剩余血量
+  /// </summary>
+  public sealed partial class CSReportUserHP : pb::IMessage<CSReportUserHP> {
+    private static readonly pb::MessageParser<CSReportUserHP> _parser = new pb::MessageParser<CSReportUserHP>(() => new CSReportUserHP());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<CSReportUserHP> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::BossServer.FightReflection.Descriptor.MessageTypes[1]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CSReportUserHP() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CSReportUserHP(CSReportUserHP other) : this() {
+      userHP_ = other.userHP_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CSReportUserHP Clone() {
+      return new CSReportUserHP(this);
+    }
+
+    /// <summary>Field number for the "UserHP" field.</summary>
+    public const int UserHPFieldNumber = 1;
+    private int userHP_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int UserHP {
+      get { return userHP_; }
+      set {
+        userHP_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as CSReportUserHP);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(CSReportUserHP other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (UserHP != other.UserHP) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (UserHP != 0) hash ^= UserHP.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (UserHP != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(UserHP);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (UserHP != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(UserHP);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(CSReportUserHP other) {
+      if (other == null) {
+        return;
+      }
+      if (other.UserHP != 0) {
+        UserHP = other.UserHP;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            UserHP = input.ReadInt32();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  /// 结束(一般情况下是玩家血量到底主动退出)
+  /// </summary>
+  public sealed partial class CSLeaveFight : pb::IMessage<CSLeaveFight> {
+    private static readonly pb::MessageParser<CSLeaveFight> _parser = new pb::MessageParser<CSLeaveFight>(() => new CSLeaveFight());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<CSLeaveFight> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::BossServer.FightReflection.Descriptor.MessageTypes[2]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CSLeaveFight() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CSLeaveFight(CSLeaveFight other) : this() {
+      No_ = other.No_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CSLeaveFight Clone() {
+      return new CSLeaveFight(this);
+    }
+
+    /// <summary>Field number for the "_no" field.</summary>
+    public const int NoFieldNumber = 1;
+    private int No_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int No {
+      get { return No_; }
+      set {
+        No_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as CSLeaveFight);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(CSLeaveFight other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (No != other.No) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (No != 0) hash ^= No.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (No != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(No);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (No != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(No);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(CSLeaveFight other) {
+      if (other == null) {
+        return;
+      }
+      if (other.No != 0) {
+        No = other.No;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            No = input.ReadInt32();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  /// 从连接状态转入已经登陆状态(标记自己的uid和zoneid)
+  /// </summary>
+  public sealed partial class CSEnterFight : pb::IMessage<CSEnterFight> {
+    private static readonly pb::MessageParser<CSEnterFight> _parser = new pb::MessageParser<CSEnterFight>(() => new CSEnterFight());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<CSEnterFight> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::BossServer.FightReflection.Descriptor.MessageTypes[3]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CSEnterFight() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CSEnterFight(CSEnterFight other) : this() {
+      zoneid_ = other.zoneid_;
+      uid_ = other.uid_;
+      name_ = other.name_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public CSEnterFight Clone() {
+      return new CSEnterFight(this);
+    }
+
+    /// <summary>Field number for the "Zoneid" field.</summary>
+    public const int ZoneidFieldNumber = 1;
+    private int zoneid_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int Zoneid {
+      get { return zoneid_; }
+      set {
+        zoneid_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "Uid" field.</summary>
+    public const int UidFieldNumber = 2;
+    private string uid_ = "";
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string Uid {
+      get { return uid_; }
+      set {
+        uid_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    /// <summary>Field number for the "Name" field.</summary>
+    public const int NameFieldNumber = 3;
+    private string name_ = "";
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public string Name {
+      get { return name_; }
+      set {
+        name_ = pb::ProtoPreconditions.CheckNotNull(value, "value");
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as CSEnterFight);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(CSEnterFight other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (Zoneid != other.Zoneid) return false;
+      if (Uid != other.Uid) return false;
+      if (Name != other.Name) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (Zoneid != 0) hash ^= Zoneid.GetHashCode();
+      if (Uid.Length != 0) hash ^= Uid.GetHashCode();
+      if (Name.Length != 0) hash ^= Name.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (Zoneid != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(Zoneid);
+      }
+      if (Uid.Length != 0) {
+        output.WriteRawTag(18);
+        output.WriteString(Uid);
+      }
+      if (Name.Length != 0) {
+        output.WriteRawTag(26);
+        output.WriteString(Name);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (Zoneid != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(Zoneid);
+      }
+      if (Uid.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Uid);
+      }
+      if (Name.Length != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeStringSize(Name);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(CSEnterFight other) {
+      if (other == null) {
+        return;
+      }
+      if (other.Zoneid != 0) {
+        Zoneid = other.Zoneid;
+      }
+      if (other.Uid.Length != 0) {
+        Uid = other.Uid;
+      }
+      if (other.Name.Length != 0) {
+        Name = other.Name;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            Zoneid = input.ReadInt32();
+            break;
+          }
+          case 18: {
+            Uid = input.ReadString();
+            break;
+          }
+          case 26: {
+            Name = input.ReadString();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  /// 登陆完成
+  /// </summary>
+  public sealed partial class SCEnterFight : pb::IMessage<SCEnterFight> {
+    private static readonly pb::MessageParser<SCEnterFight> _parser = new pb::MessageParser<SCEnterFight>(() => new SCEnterFight());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<SCEnterFight> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::BossServer.FightReflection.Descriptor.MessageTypes[4]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SCEnterFight() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SCEnterFight(SCEnterFight other) : this() {
+      bossHp_ = other.bossHp_;
+      bossMaxHp_ = other.bossMaxHp_;
+      userHP_ = other.userHP_;
+      countDown_ = other.countDown_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SCEnterFight Clone() {
+      return new SCEnterFight(this);
+    }
+
+    /// <summary>Field number for the "BossHp" field.</summary>
+    public const int BossHpFieldNumber = 1;
+    private int bossHp_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int BossHp {
+      get { return bossHp_; }
+      set {
+        bossHp_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "BossMaxHp" field.</summary>
+    public const int BossMaxHpFieldNumber = 2;
+    private int bossMaxHp_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int BossMaxHp {
+      get { return bossMaxHp_; }
+      set {
+        bossMaxHp_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "UserHP" field.</summary>
+    public const int UserHPFieldNumber = 3;
+    private int userHP_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int UserHP {
+      get { return userHP_; }
+      set {
+        userHP_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "CountDown" field.</summary>
+    public const int CountDownFieldNumber = 4;
+    private int countDown_;
+    /// <summary>
+    /// 倒计时
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CountDown {
+      get { return countDown_; }
+      set {
+        countDown_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as SCEnterFight);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(SCEnterFight other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (BossHp != other.BossHp) return false;
+      if (BossMaxHp != other.BossMaxHp) return false;
+      if (UserHP != other.UserHP) return false;
+      if (CountDown != other.CountDown) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (BossHp != 0) hash ^= BossHp.GetHashCode();
+      if (BossMaxHp != 0) hash ^= BossMaxHp.GetHashCode();
+      if (UserHP != 0) hash ^= UserHP.GetHashCode();
+      if (CountDown != 0) hash ^= CountDown.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (BossHp != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(BossHp);
+      }
+      if (BossMaxHp != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(BossMaxHp);
+      }
+      if (UserHP != 0) {
+        output.WriteRawTag(24);
+        output.WriteInt32(UserHP);
+      }
+      if (CountDown != 0) {
+        output.WriteRawTag(32);
+        output.WriteInt32(CountDown);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (BossHp != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(BossHp);
+      }
+      if (BossMaxHp != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(BossMaxHp);
+      }
+      if (UserHP != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(UserHP);
+      }
+      if (CountDown != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(CountDown);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(SCEnterFight other) {
+      if (other == null) {
+        return;
+      }
+      if (other.BossHp != 0) {
+        BossHp = other.BossHp;
+      }
+      if (other.BossMaxHp != 0) {
+        BossMaxHp = other.BossMaxHp;
+      }
+      if (other.UserHP != 0) {
+        UserHP = other.UserHP;
+      }
+      if (other.CountDown != 0) {
+        CountDown = other.CountDown;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            BossHp = input.ReadInt32();
+            break;
+          }
+          case 16: {
+            BossMaxHp = input.ReadInt32();
+            break;
+          }
+          case 24: {
+            UserHP = input.ReadInt32();
+            break;
+          }
+          case 32: {
+            CountDown = input.ReadInt32();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  /// 更新状态数据
+  /// </summary>
+  public sealed partial class SCUpdateProperties : pb::IMessage<SCUpdateProperties> {
+    private static readonly pb::MessageParser<SCUpdateProperties> _parser = new pb::MessageParser<SCUpdateProperties>(() => new SCUpdateProperties());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<SCUpdateProperties> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::BossServer.FightReflection.Descriptor.MessageTypes[5]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SCUpdateProperties() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SCUpdateProperties(SCUpdateProperties other) : this() {
+      bossHp_ = other.bossHp_;
+      bossMaxHp_ = other.bossMaxHp_;
+      countDown_ = other.countDown_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SCUpdateProperties Clone() {
+      return new SCUpdateProperties(this);
+    }
+
+    /// <summary>Field number for the "BossHp" field.</summary>
+    public const int BossHpFieldNumber = 1;
+    private int bossHp_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int BossHp {
+      get { return bossHp_; }
+      set {
+        bossHp_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "BossMaxHp" field.</summary>
+    public const int BossMaxHpFieldNumber = 2;
+    private int bossMaxHp_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int BossMaxHp {
+      get { return bossMaxHp_; }
+      set {
+        bossMaxHp_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "CountDown" field.</summary>
+    public const int CountDownFieldNumber = 3;
+    private int countDown_;
+    /// <summary>
+    /// 倒计时
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CountDown {
+      get { return countDown_; }
+      set {
+        countDown_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as SCUpdateProperties);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(SCUpdateProperties other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (BossHp != other.BossHp) return false;
+      if (BossMaxHp != other.BossMaxHp) return false;
+      if (CountDown != other.CountDown) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (BossHp != 0) hash ^= BossHp.GetHashCode();
+      if (BossMaxHp != 0) hash ^= BossMaxHp.GetHashCode();
+      if (CountDown != 0) hash ^= CountDown.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (BossHp != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(BossHp);
+      }
+      if (BossMaxHp != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(BossMaxHp);
+      }
+      if (CountDown != 0) {
+        output.WriteRawTag(24);
+        output.WriteInt32(CountDown);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (BossHp != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(BossHp);
+      }
+      if (BossMaxHp != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(BossMaxHp);
+      }
+      if (CountDown != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(CountDown);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(SCUpdateProperties other) {
+      if (other == null) {
+        return;
+      }
+      if (other.BossHp != 0) {
+        BossHp = other.BossHp;
+      }
+      if (other.BossMaxHp != 0) {
+        BossMaxHp = other.BossMaxHp;
+      }
+      if (other.CountDown != 0) {
+        CountDown = other.CountDown;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            BossHp = input.ReadInt32();
+            break;
+          }
+          case 16: {
+            BossMaxHp = input.ReadInt32();
+            break;
+          }
+          case 24: {
+            CountDown = input.ReadInt32();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  /// <summary>
+  /// 战斗结束
+  /// </summary>
+  public sealed partial class SCGameOver : pb::IMessage<SCGameOver> {
+    private static readonly pb::MessageParser<SCGameOver> _parser = new pb::MessageParser<SCGameOver>(() => new SCGameOver());
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pb::MessageParser<SCGameOver> Parser { get { return _parser; } }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public static pbr::MessageDescriptor Descriptor {
+      get { return global::BossServer.FightReflection.Descriptor.MessageTypes[6]; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    pbr::MessageDescriptor pb::IMessage.Descriptor {
+      get { return Descriptor; }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SCGameOver() {
+      OnConstruction();
+    }
+
+    partial void OnConstruction();
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SCGameOver(SCGameOver other) : this() {
+      bossHp_ = other.bossHp_;
+      countDown_ = other.countDown_;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public SCGameOver Clone() {
+      return new SCGameOver(this);
+    }
+
+    /// <summary>Field number for the "BossHp" field.</summary>
+    public const int BossHpFieldNumber = 1;
+    private int bossHp_;
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int BossHp {
+      get { return bossHp_; }
+      set {
+        bossHp_ = value;
+      }
+    }
+
+    /// <summary>Field number for the "CountDown" field.</summary>
+    public const int CountDownFieldNumber = 2;
+    private int countDown_;
+    /// <summary>
+    /// 倒计时
+    /// </summary>
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CountDown {
+      get { return countDown_; }
+      set {
+        countDown_ = value;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override bool Equals(object other) {
+      return Equals(other as SCGameOver);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public bool Equals(SCGameOver other) {
+      if (ReferenceEquals(other, null)) {
+        return false;
+      }
+      if (ReferenceEquals(other, this)) {
+        return true;
+      }
+      if (BossHp != other.BossHp) return false;
+      if (CountDown != other.CountDown) return false;
+      return true;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override int GetHashCode() {
+      int hash = 1;
+      if (BossHp != 0) hash ^= BossHp.GetHashCode();
+      if (CountDown != 0) hash ^= CountDown.GetHashCode();
+      return hash;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public override string ToString() {
+      return pb::JsonFormatter.ToDiagnosticString(this);
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void WriteTo(pb::CodedOutputStream output) {
+      if (BossHp != 0) {
+        output.WriteRawTag(8);
+        output.WriteInt32(BossHp);
+      }
+      if (CountDown != 0) {
+        output.WriteRawTag(16);
+        output.WriteInt32(CountDown);
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public int CalculateSize() {
+      int size = 0;
+      if (BossHp != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(BossHp);
+      }
+      if (CountDown != 0) {
+        size += 1 + pb::CodedOutputStream.ComputeInt32Size(CountDown);
+      }
+      return size;
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(SCGameOver other) {
+      if (other == null) {
+        return;
+      }
+      if (other.BossHp != 0) {
+        BossHp = other.BossHp;
+      }
+      if (other.CountDown != 0) {
+        CountDown = other.CountDown;
+      }
+    }
+
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute]
+    public void MergeFrom(pb::CodedInputStream input) {
+      uint tag;
+      while ((tag = input.ReadTag()) != 0) {
+        switch(tag) {
+          default:
+            input.SkipLastField();
+            break;
+          case 8: {
+            BossHp = input.ReadInt32();
+            break;
+          }
+          case 16: {
+            CountDown = input.ReadInt32();
+            break;
+          }
+        }
+      }
+    }
+
+  }
+
+  #endregion
+
+}
+
+#endregion Designer generated code

+ 91 - 0
CSserver/PBReferens/pbcs/MsgTypeEnum.cs

@@ -0,0 +1,91 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: pb/MsgTypeEnum.proto
+#pragma warning disable 1591, 0612, 3021
+#region Designer generated code
+
+using pb = global::Google.Protobuf;
+using pbc = global::Google.Protobuf.Collections;
+using pbr = global::Google.Protobuf.Reflection;
+using scg = global::System.Collections.Generic;
+/// <summary>Holder for reflection information generated from pb/MsgTypeEnum.proto</summary>
+public static partial class MsgTypeEnumReflection {
+
+  #region Descriptor
+  /// <summary>File descriptor for pb/MsgTypeEnum.proto</summary>
+  public static pbr::FileDescriptor Descriptor {
+    get { return descriptor; }
+  }
+  private static pbr::FileDescriptor descriptor;
+
+  static MsgTypeEnumReflection() {
+    byte[] descriptorData = global::System.Convert.FromBase64String(
+        string.Concat(
+          "ChRwYi9Nc2dUeXBlRW51bS5wcm90byrpAQoQZVByb3RvY2FsQ29tbWFuZBII",
+          "CgRub29wEAASDAoIQ1NfTG9naW4QARITCg9DU19SZXBvcnREYW1hZ2UQAhIQ",
+          "CgxDU19MZWF2ZVJvb20QAxITCg9DU19SZXBvcnRVc2VySHAQBBIQCgxDU19D",
+          "aGF0TG9naW4QChISCg5DU19DaGF0U2VuZE1zZxALEgwKCFNDX0xvZ2luEGQS",
+          "FwoTU0NfVXBkYXRlUHJvcGVydGllcxBlEg8KC1NDX0dhbWVPdmVyEGYSEAoM",
+          "U0NfQ2hhdExvZ2luEG4SEQoNU0NfQ2hhdE5ld01zZxBvYgZwcm90bzM="));
+    descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
+        new pbr::FileDescriptor[] { },
+        new pbr::GeneratedClrTypeInfo(new[] {typeof(global::eProtocalCommand), }, null));
+  }
+  #endregion
+
+}
+#region Enums
+public enum eProtocalCommand {
+  /// <summary>
+  /// 无操作
+  /// </summary>
+  [pbr::OriginalName("noop")] Noop = 0,
+  /// <summary>
+  /// 登录()
+  /// </summary>
+  [pbr::OriginalName("CS_Login")] CsLogin = 1,
+  /// <summary>
+  /// 上报伤害输出
+  /// </summary>
+  [pbr::OriginalName("CS_ReportDamage")] CsReportDamage = 2,
+  /// <summary>
+  /// 离开房间(战斗失败结束)
+  /// </summary>
+  [pbr::OriginalName("CS_LeaveRoom")] CsLeaveRoom = 3,
+  /// <summary>
+  /// 上报玩家剩余血量
+  /// </summary>
+  [pbr::OriginalName("CS_ReportUserHp")] CsReportUserHp = 4,
+  /// <summary>
+  /// 聊天, 客户端初始化
+  /// </summary>
+  [pbr::OriginalName("CS_ChatLogin")] CsChatLogin = 10,
+  /// <summary>
+  /// 聊天, 发送消息
+  /// </summary>
+  [pbr::OriginalName("CS_ChatSendMsg")] CsChatSendMsg = 11,
+  /// <summary>
+  /// 登录结果
+  /// </summary>
+  [pbr::OriginalName("SC_Login")] ScLogin = 100,
+  /// <summary>
+  /// 更新数据
+  /// </summary>
+  [pbr::OriginalName("SC_UpdateProperties")] ScUpdateProperties = 101,
+  /// <summary>
+  /// 游戏结束
+  /// </summary>
+  [pbr::OriginalName("SC_GameOver")] ScGameOver = 102,
+  /// <summary>
+  /// 聊天, 登录成功
+  /// </summary>
+  [pbr::OriginalName("SC_ChatLogin")] ScChatLogin = 110,
+  /// <summary>
+  /// 聊天, 新消息
+  /// </summary>
+  [pbr::OriginalName("SC_ChatNewMsg")] ScChatNewMsg = 111,
+}
+
+#endregion
+
+
+#endregion Designer generated code

+ 252 - 0
CSserver/clientTest/Program.cs

@@ -0,0 +1,252 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Threading.Channels;
+using System.Linq;
+using System.Net.Http;
+using System.Collections.Generic;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Diagnostics;
+using ProtoDataBuff;
+using BossServer;
+using pb = global::Google.Protobuf;
+
+namespace clientTest.bossfight
+{
+    
+    class Program
+    {
+        static readonly Random r = new Random();
+
+        /// <summary>
+        /// 消息分发
+        /// </summary>
+        static private Dictionary<eProtocalCommand, Action<sSocketData>> callbacks = new Dictionary<eProtocalCommand, Action<sSocketData>>();
+        static void Main(string[] args)
+        {
+
+            callbacks.Add(eProtocalCommand.ScUpdateProperties, On_update);
+            callbacks.Add(eProtocalCommand.ScGameOver, On_GameOver);
+            var n = 1;
+            var list = new Task[n];
+            for (int i = 0; i < n; i++)
+            {
+                list[i] = Task.Run(async () => await send());
+            }
+            Task.Run(Dispatch);
+
+            Task.WaitAll(list);
+        }
+
+        /// <summary>
+        /// 处理客户端上报伤害请求
+        /// </summary>
+        static void On_update(sSocketData data)
+        {
+            var msg = SCUpdateProperties.Parser.ParseFrom(data._data);
+
+            Console.WriteLine("最新HP: " + msg.BossHp);
+        }
+
+        /// <summary>
+        /// 处理客户端上报伤害请求
+        /// </summary>
+        static void On_GameOver(sSocketData data)
+        {
+            var msg = SCGameOver.Parser.ParseFrom(data._data);
+            Console.WriteLine("战斗结束: " + msg.BossHp);
+            Task.Delay(3000).ContinueWith(t => Environment.Exit(0));
+        }
+        static async Task send()
+        {
+
+            var port = 6002;
+            var endPoint = new IPEndPoint(IPAddress.Parse("192.168.10.86"), port);
+            //var endPoint = new IPEndPoint(IPAddress.Parse("115.159.121.129"), port);
+
+            using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
+            {
+                try
+                {
+                  await  client.ConnectAsync(endPoint);
+                }
+                catch (Exception ee)
+                {
+                    Debug.WriteLine(ee.Message);
+                }
+                var t = Task.Run(() => recv(client));
+                await login(client);
+                while (true)
+                {
+                    try
+                    {
+                        await ReportDamage(client);
+                        Thread.Sleep(r.Next(1000, 3000));
+                    }
+                    catch (Exception e)
+                    {
+                        client.Close();
+                        break;
+                    }
+                }
+            }
+        }
+
+        async static Task login(Socket Sock)
+        {
+            try
+            {
+                var msg = new CSEnterFight() { Uid = Guid.NewGuid().ToString(), Zoneid = 1 };
+                var data = SocketDataToBytes(BytesToSocketData(eProtocalCommand.CsLogin, IMsg2Bytes(msg)));
+                await Sock.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
+            }
+            catch (Exception e)
+            {
+                Sock.Close();
+            }
+        }
+
+        async static Task ReportDamage(Socket sock)
+        {
+            var msg = new CSFightReportDamage() { Damage = r.Next(-320, 0) };
+
+            var data = SocketDataToBytes(BytesToSocketData(eProtocalCommand.CsReportDamage, IMsg2Bytes(msg)));
+            await sock.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
+        }
+
+        static byte[] IMsg2Bytes(pb::IMessage msg)
+        {
+            using var ms = new MemoryStream();
+            using var goutstream = new pb::CodedOutputStream(ms);
+            msg.WriteTo(goutstream);
+            goutstream.Flush();
+            ms.Seek(0, SeekOrigin.Begin);
+            return ms.ToArray();
+        }
+
+
+
+        static async void Dispatch()
+        {
+            while (true)
+            {
+                var msg = await recvDataBuffer.Reader.ReadAsync();
+                if (callbacks.ContainsKey(msg._protocallType))
+                {
+                    callbacks[msg._protocallType](msg);
+                }
+                else
+                {
+                    // 未找到消息处理逻辑
+                    Console.WriteLine("未识别的消息类型:" + msg._protocallType.ToString());
+                }
+            }
+        }
+
+        /// <summary>
+        /// 向客户端写入消息
+        /// </summary>
+        static async void WriteToserver(Socket Sock)
+        {
+            while (true)
+            {
+                var msg = await sendDataBuffer.Reader.ReadAsync();
+                var data = SocketDataToBytes(msg);
+                await Sock.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
+            }
+        }
+        /// <summary>
+        /// 网络结构转数据
+        /// </summary>
+        /// <param name="tmpSocketData"></param>
+        /// <returns></returns>
+        static private byte[] SocketDataToBytes(sSocketData tmpSocketData)
+        {
+            byte[] _tmpBuff = new byte[tmpSocketData._buffLength];
+            byte[] _tmpBuffLength = BitConverter.GetBytes(tmpSocketData._buffLength);
+            byte[] _tmpDataLenght = BitConverter.GetBytes((UInt16)tmpSocketData._protocallType);
+
+            Array.Copy(_tmpBuffLength, 0, _tmpBuff, 0, Constants.HEAD_DATA_LEN);//缓存总长度
+            Array.Copy(_tmpDataLenght, 0, _tmpBuff, Constants.HEAD_DATA_LEN, Constants.HEAD_TYPE_LEN);//协议类型
+            Array.Copy(tmpSocketData._data, 0, _tmpBuff, Constants.HEAD_LEN, tmpSocketData._dataLength);//协议数据
+
+            return _tmpBuff;
+        }
+
+
+        /// <summary>
+        /// 数据转网络结构
+        /// </summary>
+        /// <param name="_protocalType"></param>
+        /// <param name="_data"></param>
+        /// <returns></returns>
+        static private sSocketData BytesToSocketData(eProtocalCommand _protocalType, byte[] _data)
+        {
+            sSocketData tmpSocketData = new sSocketData();
+            tmpSocketData._buffLength = Constants.HEAD_LEN + _data.Length;
+            tmpSocketData._dataLength = _data.Length;
+            tmpSocketData._protocallType = _protocalType;
+            tmpSocketData._data = _data;
+            return tmpSocketData;
+        }
+
+
+        /// <summary>
+        /// 接收buffer
+        /// </summary>
+        static private Channel<sSocketData> recvDataBuffer = Channel.CreateUnbounded<sSocketData>();
+        /// <summary>
+        /// 发送buffer
+        /// </summary>
+        static private Channel<sSocketData> sendDataBuffer = Channel.CreateUnbounded<sSocketData>();
+
+        /// <summary>
+        /// 接收客户端发来的信息,客户端套接字对象
+        /// </summary>
+        /// <param name="socketclientpara"></param>    
+        static async void recv(Socket socketServer)
+        {
+            socketServer.ReceiveTimeout = 800;                                             // 接收等待超时时间设为800毫秒
+            var _databuffer = new DataBuffer();
+            byte[] arrServerRecMsg = new byte[4096];                                       // 创建一个内存缓冲区,其大小为4k字节  
+            while (true)
+            {
+                try
+                {
+                    var length = await socketServer.ReceiveAsync(new ArraySegment<byte>(arrServerRecMsg), SocketFlags.None);   // 将接收到的信息存入到内存缓冲区,并返回其字节数组的长度    
+                    if (length <= 0)                                                      // 视为客户端已经close连接
+                    {
+                        break;
+                    }
+                    _databuffer.AddBuffer(arrServerRecMsg, length);                        //将收到的数据添加到缓存器中
+                    while (_databuffer.GetData(out sSocketData _socketData))                         //取出一条完整数据
+                    {
+                        await recvDataBuffer.Writer.WriteAsync(_socketData);                 // 放入channel
+                    }
+                    Array.Clear(arrServerRecMsg, 0, length);
+                }
+                catch (SocketException e)
+                {
+                    if (e.ErrorCode == 10060)                                          // 超时的时候错误号码是10060
+                    {
+                        continue;                                                      // 继续等待  
+                    }
+                    break;
+                }
+                catch (Exception)
+                {
+                    break;
+                }
+            }
+
+            socketServer.Close();                                                     // 关闭之前accept出来的和客户端进行通信的套接字 
+        }
+    }
+
+
+}

+ 313 - 0
CSserver/clientTest/Program_Chat.cs

@@ -0,0 +1,313 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Threading.Channels;
+using System.Linq;
+using System.Net.Http;
+using System.Collections.Generic;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Diagnostics;
+using ProtoDataBuff;
+using Chat;
+using pb = global::Google.Protobuf;
+
+namespace clientTest.chat
+{
+    public static class TaskWaitingExtensions
+    {
+        public static async Task<TResult> WaitAsync<TResult>(this Task<TResult> task, TimeSpan timeout)
+        {
+            using (var timeoutCancellationTokenSource = new CancellationTokenSource())
+            {
+                var delayTask = Task.Delay(timeout, timeoutCancellationTokenSource.Token);
+                if (await Task.WhenAny(task, delayTask) == task)
+                {
+                    timeoutCancellationTokenSource.Cancel();
+                    return await task;
+                }
+                throw new TimeoutException("The operation has timed out.");
+            }
+        }
+        public static async Task WaitAsync(this Task task, TimeSpan timeout)
+        {
+            using (var timeoutCancellationTokenSource = new CancellationTokenSource())
+            {
+                var delayTask = Task.Delay(timeout, timeoutCancellationTokenSource.Token);
+                if (await Task.WhenAny(task, delayTask) == task)
+                {
+                    timeoutCancellationTokenSource.Cancel();
+                    return;
+                }
+                throw new TimeoutException("The operation has timed out.");
+            }
+        }
+    }
+    class Program
+    {
+        static readonly string[] names = new string[] { "你猜(尼采)", "黑哥儿(黑格尔)", "孟德四舅" };
+        static readonly string[] texts = new string[] {
+            "一个人可以失败很多次,但是只要他没有开始责怪旁人,他还不是一个失败者。",
+            "这个世界既不是有钱人的世界,也不是有权人的世界,它是有心人的世界。",
+            "伟人之所以伟大,是因为他与别人共处逆境时,别人失去了信心,他却下决心实现自己的目标。",
+            "世上没有绝望的处境,只有对处境绝望的人。 ",
+            "时间就像一张网,你撒在哪里,收获就在哪里。",
+            "心若计较,处处都是怨言;心不计较,时时都是晴天。",
+            "能使我们感觉快乐的,不是环境,而是态度。",
+            "学问是用来实践的,不是拿来用嘴说的。",
+            "自己打败自己是最可悲的失败,自己战胜自己是最可贵的胜利。",
+            "我们得成功,其实成功有一条很简单的定律:“只要站起来的次数比被击倒的次数多一次就行。",
+            " 弱者坐待良机,强者创造时机。",
+            " 没有不会做的事, 只有不想做的事。",
+            "任何的限制, 都是从自己内心开始的。",
+            "只要还有明天, 今日就永远是起跑线。",
+            "既然认准一条道路, 何必去打听要走多久。",
+            "现在站在什么地方不重要,重要的是你往什么方向移动?",
+            "如果什么都想要,只会什么都得不到。"
+
+        };
+        static readonly Random r = new Random();
+
+        /// <summary>
+        /// 消息分发
+        /// </summary>
+        static private Dictionary<eProtocalCommand, Action<sSocketData>> callbacks = new Dictionary<eProtocalCommand, Action<sSocketData>>();
+        static void Main(string[] args)
+        {
+
+            callbacks.Add(eProtocalCommand.ScChatNewMsg, On_update);
+            callbacks.Add(eProtocalCommand.ScChatLogin, On_LoginOver);
+            var n = 10;
+            var list = new Task[n];
+            for (int i = 0; i < n; i++)
+            {
+                list[i] = Task.Run(async () => await send());
+            }
+            Task.Run(Dispatch);
+
+            Task.WaitAll(list);
+        }
+
+        static string ChannelName(ChatChannel c) => c switch
+        {
+            ChatChannel.System => "系统",
+            ChatChannel.World => "世界",
+            ChatChannel.Guild => "公会",
+            _ => "x",
+        };
+
+        /// <summary>
+        /// 处理客户端上报伤害请求
+        /// </summary>
+        static void On_update(sSocketData data)
+        {
+            var msg = SC_ChatNewMsg.Parser.ParseFrom(data._data);
+
+            Console.WriteLine($"[{ChannelName(msg.FromChannel)}]-[{msg.SenderName}] : " + msg.Msg);
+        }
+
+        /// <summary>
+        /// 处理客户端上报伤害请求
+        /// </summary>
+        static void On_LoginOver(sSocketData data)
+        {
+            var msg = SC_ChatLogin.Parser.ParseFrom(data._data);
+            Console.WriteLine("登录结果: " + msg.Code);
+            //Task.Delay(3000).ContinueWith(t => Environment.Exit(0));
+        }
+        static async Task send()
+        {
+
+            var port = 6000;
+            var endPoint = new IPEndPoint(IPAddress.Parse("192.168.10.86"), port);
+            //var endPoint = new IPEndPoint(IPAddress.Parse("115.159.121.129"), port);
+
+            using (var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
+            {
+                try
+                {
+                    await client.ConnectAsync(endPoint);
+                }
+                catch (Exception ee)
+                {
+                    Debug.WriteLine(ee.Message);
+                }
+                var t = Task.Run(() => recv(client));
+                await login(client);
+                var i = 0;
+                while (i++ < 10)
+                {
+                    try
+                    {
+                        await sendMsg(client);
+                        Thread.Sleep(r.Next(1000, 1100));
+                    }
+                    catch (Exception e)
+                    {
+                        client.Close();
+                        break;
+                    }
+                }
+            }
+        }
+
+        async static Task login(Socket Sock)
+        {
+            try
+            {
+                var msg = new CS_ChatLogin() { Uid = Guid.NewGuid().ToString(), Zoneid = 1, Name = names[r.Next(0, 3)] + r.Next() };
+                var data = SocketDataToBytes(BytesToSocketData(eProtocalCommand.CsChatLogin, IMsg2Bytes(msg)));
+                await Sock.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
+            }
+            catch (Exception e)
+            {
+                Sock.Close();
+            }
+        }
+
+        async static Task sendMsg(Socket sock)
+        {
+            var c = (ChatChannel)r.Next(0, 3);
+
+            var info = texts[r.Next(0, texts.Length)];
+            var msg = new CS_ChatSendMsg() { ToChannel = c, Msg = info };
+
+            var data = SocketDataToBytes(BytesToSocketData(eProtocalCommand.CsChatSendMsg, IMsg2Bytes(msg)));
+            await sock.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
+        }
+
+        static byte[] IMsg2Bytes(pb::IMessage msg)
+        {
+            using var ms = new MemoryStream();
+            using var goutstream = new pb::CodedOutputStream(ms);
+            msg.WriteTo(goutstream);
+            goutstream.Flush();
+            ms.Seek(0, SeekOrigin.Begin);
+            return ms.ToArray();
+        }
+
+
+
+        static async void Dispatch()
+        {
+            while (true)
+            {
+                var msg = await recvDataBuffer.Reader.ReadAsync();
+                if (callbacks.ContainsKey(msg._protocallType))
+                {
+                    callbacks[msg._protocallType](msg);
+                }
+                else
+                {
+                    // 未找到消息处理逻辑
+                    Console.WriteLine("未识别的消息类型:" + msg._protocallType.ToString());
+                }
+            }
+        }
+
+        /// <summary>
+        /// 向客户端写入消息
+        /// </summary>
+        static async void WriteToserver(Socket Sock)
+        {
+            while (true)
+            {
+                var msg = await sendDataBuffer.Reader.ReadAsync();
+                var data = SocketDataToBytes(msg);
+                await Sock.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
+            }
+        }
+        /// <summary>
+        /// 网络结构转数据
+        /// </summary>
+        /// <param name="tmpSocketData"></param>
+        /// <returns></returns>
+        static private byte[] SocketDataToBytes(sSocketData tmpSocketData)
+        {
+            byte[] _tmpBuff = new byte[tmpSocketData._buffLength];
+            byte[] _tmpBuffLength = BitConverter.GetBytes(tmpSocketData._buffLength);
+            byte[] _tmpDataLenght = BitConverter.GetBytes((UInt16)tmpSocketData._protocallType);
+
+            Array.Copy(_tmpBuffLength, 0, _tmpBuff, 0, Constants.HEAD_DATA_LEN);//缓存总长度
+            Array.Copy(_tmpDataLenght, 0, _tmpBuff, Constants.HEAD_DATA_LEN, Constants.HEAD_TYPE_LEN);//协议类型
+            Array.Copy(tmpSocketData._data, 0, _tmpBuff, Constants.HEAD_LEN, tmpSocketData._dataLength);//协议数据
+
+            return _tmpBuff;
+        }
+
+
+        /// <summary>
+        /// 数据转网络结构
+        /// </summary>
+        /// <param name="_protocalType"></param>
+        /// <param name="_data"></param>
+        /// <returns></returns>
+        static private sSocketData BytesToSocketData(eProtocalCommand _protocalType, byte[] _data)
+        {
+            sSocketData tmpSocketData = new sSocketData();
+            tmpSocketData._buffLength = Constants.HEAD_LEN + _data.Length;
+            tmpSocketData._dataLength = _data.Length;
+            tmpSocketData._protocallType = _protocalType;
+            tmpSocketData._data = _data;
+            return tmpSocketData;
+        }
+
+
+        /// <summary>
+        /// 接收buffer
+        /// </summary>
+        static private Channel<sSocketData> recvDataBuffer = Channel.CreateUnbounded<sSocketData>();
+        /// <summary>
+        /// 发送buffer
+        /// </summary>
+        static private Channel<sSocketData> sendDataBuffer = Channel.CreateUnbounded<sSocketData>();
+
+        /// <summary>
+        /// 接收客户端发来的信息,客户端套接字对象
+        /// </summary>
+        /// <param name="socketclientpara"></param>    
+        static async void recv(Socket socketServer)
+        {
+            socketServer.ReceiveTimeout = 800;                                             // 接收等待超时时间设为800毫秒
+            var _databuffer = new DataBuffer();
+            byte[] arrServerRecMsg = new byte[4096];                                       // 创建一个内存缓冲区,其大小为4k字节  
+            while (true)
+            {
+                try
+                {
+                    var length = await socketServer.ReceiveAsync(new ArraySegment<byte>(arrServerRecMsg), SocketFlags.None);   // 将接收到的信息存入到内存缓冲区,并返回其字节数组的长度    
+                    if (length <= 0)                                                      // 视为客户端已经close连接
+                    {
+                        break;
+                    }
+                    _databuffer.AddBuffer(arrServerRecMsg, length);                        //将收到的数据添加到缓存器中
+                    while (_databuffer.GetData(out sSocketData _socketData))                         //取出一条完整数据
+                    {
+                        await recvDataBuffer.Writer.WriteAsync(_socketData);                 // 放入channel
+                    }
+                    Array.Clear(arrServerRecMsg, 0, length);
+                }
+                catch (SocketException e)
+                {
+                    if (e.ErrorCode == 10060)                                          // 超时的时候错误号码是10060
+                    {
+                        continue;                                                      // 继续等待  
+                    }
+                    break;
+                }
+                catch (Exception)
+                {
+                    break;
+                }
+            }
+
+            socketServer.Close();                                                     // 关闭之前accept出来的和客户端进行通信的套接字 
+        }
+    }
+
+
+}

+ 18 - 0
CSserver/clientTest/clientTest.csproj

@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+    <StartupObject>clientTest.chat.Program</StartupObject>
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Lib1\Lib1.csproj" />
+    <ProjectReference Include="..\PBReferens\PBReferens.csproj" />
+  </ItemGroup>
+
+</Project>

+ 55 - 0
CSserver/csserver.sln

@@ -0,0 +1,55 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31410.414
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatServer", "csserver\ChatServer.csproj", "{ADB21DA6-E0B4-4218-85F4-59351E635488}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BossServer", "BossServer\BossServer.csproj", "{005AEE5F-3437-4285-9BAA-5B056D07AB5B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "clientTest", "clientTest\clientTest.csproj", "{5FB5814B-6E21-402C-98F3-2E2DA96A8772}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PBReferens", "PBReferens\PBReferens.csproj", "{03F69CA0-352E-4F6B-A683-5C395F29A3EE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lib1", "Lib1\Lib1.csproj", "{D49EB2E3-949C-48AF-BFCA-E07B171E2715}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libs", "Libs", "{02C64572-AF0B-4AE0-8685-3836A0FD1C1E}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{ADB21DA6-E0B4-4218-85F4-59351E635488}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{ADB21DA6-E0B4-4218-85F4-59351E635488}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{ADB21DA6-E0B4-4218-85F4-59351E635488}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{ADB21DA6-E0B4-4218-85F4-59351E635488}.Release|Any CPU.Build.0 = Release|Any CPU
+		{005AEE5F-3437-4285-9BAA-5B056D07AB5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{005AEE5F-3437-4285-9BAA-5B056D07AB5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{005AEE5F-3437-4285-9BAA-5B056D07AB5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{005AEE5F-3437-4285-9BAA-5B056D07AB5B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{5FB5814B-6E21-402C-98F3-2E2DA96A8772}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{5FB5814B-6E21-402C-98F3-2E2DA96A8772}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{5FB5814B-6E21-402C-98F3-2E2DA96A8772}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{5FB5814B-6E21-402C-98F3-2E2DA96A8772}.Release|Any CPU.Build.0 = Release|Any CPU
+		{03F69CA0-352E-4F6B-A683-5C395F29A3EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{03F69CA0-352E-4F6B-A683-5C395F29A3EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{03F69CA0-352E-4F6B-A683-5C395F29A3EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{03F69CA0-352E-4F6B-A683-5C395F29A3EE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D49EB2E3-949C-48AF-BFCA-E07B171E2715}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D49EB2E3-949C-48AF-BFCA-E07B171E2715}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D49EB2E3-949C-48AF-BFCA-E07B171E2715}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D49EB2E3-949C-48AF-BFCA-E07B171E2715}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{03F69CA0-352E-4F6B-A683-5C395F29A3EE} = {02C64572-AF0B-4AE0-8685-3836A0FD1C1E}
+		{D49EB2E3-949C-48AF-BFCA-E07B171E2715} = {02C64572-AF0B-4AE0-8685-3836A0FD1C1E}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {C4A6CBBD-8341-4284-B0F8-D6CEC4B9B603}
+	EndGlobalSection
+EndGlobal

+ 13 - 0
CSserver/csserver/ChatServer.csproj

@@ -0,0 +1,13 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>net6.0</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\Lib1\Lib1.csproj" />
+    <ProjectReference Include="..\PBReferens\PBReferens.csproj" />
+  </ItemGroup>
+
+</Project>

+ 174 - 0
CSserver/csserver/Program.cs

@@ -0,0 +1,174 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Threading.Channels;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using ProtoDataBuff;
+using ChatServer.server;
+using Chat;
+namespace ChatServer
+{
+    class Program
+    {
+        const int port = 6000;                                                  // 端口号(用来监听的)
+        static Socket SocketWatch = null;                                       // 创建一个和客户端通信的套接字
+        /// <summary>
+        /// peer管理集合
+        /// </summary>
+        static public Dictionary<string, Peer> Peers = new Dictionary<string, Peer>();
+        /// <summary>
+        /// 消息分发
+        /// </summary>
+        private static Dictionary<eProtocalCommand, Action<string, sSocketData>> callbacks = new Dictionary<eProtocalCommand, Action<string, sSocketData>>();
+        static void Main(string[] args)
+        {
+            IPAddress ip = IPAddress.Any;
+            IPEndPoint ipe = new IPEndPoint(ip, port);                         //将IP地址和端口号绑定到网络节点point上  
+
+            //  callbacks.Add(eProtocalCommand.CsChatLogin, On_Login);             // 绑定处理逻辑
+            callbacks.Add(eProtocalCommand.CsChatSendMsg, On_SendMessage);     // 绑定处理逻辑
+
+            //定义一个套接字用于监听客户端发来的消息,包含三个参数(IP4寻址协议,流式连接,Tcp协议)  
+            SocketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+            SocketWatch.Bind(ipe);                                             // 监听绑定的网络节点           
+            SocketWatch.Listen(20);                                            // 将套接字的监听队列长度限制为20    
+
+            Console.WriteLine("开启监听......");
+            Console.WriteLine("ctrl + c 退出程序......");
+            Task.Run(MsgLoop);
+            Task.Run(WatchConnecting).Wait();
+            SocketWatch.Close();
+        }
+
+        internal static void AddPeer(Peer p)
+        {
+            lock (Peers)
+            {
+                Peers.Add(p.Key, p);
+            }
+        }
+        internal static void RemovePeer(string key)
+        {
+            lock (Peers)
+            {
+                Peers.Remove(key);
+            }
+        }
+
+
+        /// <summary>
+        /// 发送消息
+        /// </summary>
+        /// <param name="peerId"></param>
+        /// <param name="data"></param>
+        static void On_SendMessage(string peerKey, sSocketData data)
+        {
+            if (Peers.TryGetValue(peerKey, out Peer p))
+            {
+                var msg = CS_ChatSendMsg.Parser.ParseFrom(data._data);
+                if (null != msg)
+                {
+                    var rdb = Redis.Ins.GetDatabase(0);
+                    var sd = new SC_ChatNewMsg() { FromChannel = msg.ToChannel, SenderName = p.Name, Msg = msg.Msg };
+                    switch (msg.ToChannel)
+                    {
+                        case ChatChannel.System:    // 这个一般是客户端生成的, 除非是GM号,一般人发送这种消息拒绝转发.
+                            if (rdb.HashExists(MemKey_Cfg.GM_uids_hash, p.UID))
+                            {
+                                Peers.Values.ToList().ForEach(_p => _p.SendEvent(eProtocalCommand.ScChatNewMsg, sd));
+                            }
+                            else
+                            {
+                                Console.WriteLine($"非GM用户({p.UID})发送了系统消息转发请求!");
+                            }
+                            break;
+                        case ChatChannel.World:
+                            Peers.Values.ToList().ForEach(_p => _p.SendEvent(eProtocalCommand.ScChatNewMsg, sd));
+                            break;
+                        case ChatChannel.Guild:
+                            if (rdb.HashExists(MemKey_User.Guild(p.UID, p.zoneid), "guildId"))
+                            {
+                                var a = rdb.HashGet(MemKey_User.Guild(p.UID, p.zoneid), "guildId").ToString();
+                                var guildId = int.Parse(a);
+                                if (guildId > 0)
+                                {
+                                    JArray jArray = JArray.Parse(rdb.HashGet(MemKey_Game.Guild(p.zoneid, guildId), "members").ToString());
+                                    if (jArray.Count > 0)
+                                    {
+                                        var keys = from j in jArray select j.ToString() + ":" + p.zoneid;
+                                        Peers.Where(kv => keys.Contains(kv.Key)).ToList().ForEach(kv => kv.Value.SendEvent(eProtocalCommand.ScChatNewMsg, sd));
+                                    }
+
+                                }
+                                else
+                                {
+                                    Console.WriteLine($"找不到用户({p.UID})的公会信息");
+                                }
+                            }
+                            else
+                            {
+                                Console.WriteLine($"找不到用户({p.UID})的公会信息");
+                            }
+
+                            break;
+                        default:
+                            Console.WriteLine("chat:未知的发送目标");
+                            break;
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// 监听客户端发来的请求  
+        /// </summary>
+        async static Task WatchConnecting()
+        {
+            while (true)                                                                 // 持续不断监听客户端发来的请求     
+            {
+                Socket connection;
+                try
+                {
+                    connection = await SocketWatch.AcceptAsync();
+                }
+                catch (Exception ex)
+                {
+                    Console.WriteLine(ex.Message);                                       // 提示套接字监听异常     
+                    break;
+                }
+
+                var p = new Peer(connection);
+
+            }
+        }
+
+
+
+        /// <summary>
+        /// 客户端消息队列
+        /// </summary>
+        public static Channel<KeyValuePair<string, sSocketData>> MsgChannel = Channel.CreateBounded<KeyValuePair<string, sSocketData>>(1000);
+
+        static async void MsgLoop()
+        {
+            while (true)
+            {
+                var msg = await MsgChannel.Reader.ReadAsync();
+                if (callbacks.ContainsKey(msg.Value._protocallType))
+                {
+                    callbacks[msg.Value._protocallType](msg.Key, msg.Value);
+                }
+                else
+                {
+                    // 未找到消息处理逻辑
+                }
+            }
+        }
+    }
+}

+ 847 - 0
CSserver/csserver/db/MysqlUtil.cs

@@ -0,0 +1,847 @@
+using MySql.Data.MySqlClient;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Diagnostics;
+using static System.Diagnostics.Trace;
+using static System.String;
+
+namespace Chat
+{
+    /// <summary>
+    /// MySQL操作辅助类.
+    /// </summary>
+    public class MysqlUtil : IDisposable
+    {
+
+        /// <summary>
+        /// MySQL字段详细信息,(用于GetFiledInfo方法返回值中取信息,可以用nameof来去掉硬编码字符串以防止拼写错误)
+        /// </summary>
+        public enum EFieldInfo
+        {
+            /// <summary>
+            /// 字段类型 in .net
+            /// </summary>
+            Type,
+            /// <summary>
+            /// 是否可空
+            /// </summary>
+            Null,
+            /// <summary>
+            /// 索引类型
+            /// </summary>
+            Key,
+            /// <summary>
+            /// 默认值
+            /// </summary>
+            Default,
+            /// <summary>
+            /// 注释
+            /// </summary>
+            Comment,
+            /// <summary>
+            /// 字符集
+            /// </summary>
+            Collation,
+            /// <summary>
+            /// 额外(自增等)
+            /// </summary>
+            Extra,
+            /// <summary>
+            /// 权限
+            /// </summary>
+            Privileges
+        }
+
+        #region `    基础代码   `
+        static MysqlUtil mInst;
+        public static MysqlUtil Ins
+        {
+            get
+            {
+                if (null == mInst)
+                {
+                    mInst = new MysqlUtil();
+                }
+                return mInst;
+            }
+        }
+
+        private MysqlUtil()
+        {
+            conn = new MySqlConnection(Config.Ins.mysql.ConnectionString);
+            conn.Open();
+            Assert(conn.State == ConnectionState.Open, "连接到数据库失败");
+        }
+  
+        private MySqlConnection conn = null;
+        /// <summary>
+        /// 建立连接
+        /// </summary>
+        /// <param name="host"></param>
+        /// <param name="port"></param>
+        /// <param name="user"></param>
+        /// <param name="pwd"></param>
+        /// <param name="dbName"></param>
+        public void Connect(string host, string port, string user, string pwd, string dbName)
+        {
+            var connStr = string.Format(
+                              "server={0};port={1};user={2};password={3};database={4};Charset=utf8",
+                              host, port, user, pwd, dbName);
+            conn = new MySqlConnection(connStr);
+            conn.Open();
+            Assert(conn.State == ConnectionState.Open, "连接到数据库失败");
+        }
+        /// <summary>
+        /// 关闭连接
+        /// </summary>
+        public void Close()
+        {
+            conn.Close();
+        }
+        /// <summary>
+        /// 释放资源
+        /// </summary>
+        public void Dispose()
+        {
+            conn.Dispose();
+        }
+        #endregion
+
+        #region `    数据读    `
+
+        /// <summary>
+        /// 读取数据库表-数据操作部分逻辑需要参数中定义
+        /// 一般情况下,使用其余八个读取函数即可,如果需要自己定制数据结构,可以通过在此函数回调中自行遍历整个数据表.
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="working">action to MysqlDataReader</param>
+        public void ReadTabel(string tableName, Action<MySqlDataReader> working)
+        {
+            Assert(TableExist(tableName), "表 " + tableName + " 不存在!");
+            var sql = string.Format("SELECT * FROM {0};", tableName);
+            using (var cmd = new MySqlCommand(sql, conn))
+            using (var rdr = cmd.ExecuteReader())
+            {
+                if (null != working) working.Invoke(rdr);
+            }
+        }
+
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个JArray中, []
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// /// <param name="excludeFields">排除的字段名列表</param>
+        /// <returns>JArray</returns>
+        public JArray ReadTabelToJArray(string tableName, params string[] excludeFields)
+        {
+            var ex = new List<string>(excludeFields);
+            var arr = new JArray();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var jobj = new JObject();
+
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            jobj.Add(rdr.GetName(i), JToken.FromObject(rdr.GetValue(i)));
+                        }
+                    }
+                    arr.Add(jobj);
+                }
+            });
+            return arr;
+        }
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个JObject中, {}
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="indexFieldName">用做索引的字段名</param>
+        /// <param name="excludeFields">排除的字段名列表</param>
+        /// <returns>{}</returns>
+        public JObject ReadTabelToJObject(string tableName, string indexFieldName, out bool isArray, params string[] excludeFields)
+        {
+            Assert(HasFiledInTable(tableName, indexFieldName),
+                Format("在表[{0}]中未找到key:[{1}]", tableName, indexFieldName));
+            bool _isArray = false;
+            var ex = new List<string>(excludeFields);
+            //ex.Add(indexFieldName);
+            var obj = new JObject();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var jobj = new JObject();
+                    var id = "";
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == indexFieldName)
+                        {
+                            id = rdr.GetValue(i).ToString();
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            object v = rdr.GetValue(i);
+
+                            if (DBNull.Value == v)
+                            {
+                                var t = rdr.GetFieldType(i).Name;
+                                switch (t)
+                                {
+                                    case "Int32":
+                                        v = 0;
+                                        break;
+                                    case "String":
+                                        v = "";
+                                        break;
+                                    case "Single":
+                                        v = 0;
+                                        break;
+                                }
+                            }
+                            jobj.Add(rdr.GetName(i), JToken.FromObject(v));
+                        }
+                    }
+                    if (obj[id] == null)
+                    {  // 这段代码智能转换多值对应为[]
+                        obj.Add(id, jobj);
+                    }
+                    else
+                    {
+                        _isArray = true;
+                        var arr = obj[id] as JArray;
+                        if (null != arr)
+                        {
+                            arr.Add(jobj);
+                        }
+                        else
+                        {
+                            obj[id] = new JArray() { obj[id], jobj };
+                        }
+                    }
+                }
+
+            });
+            if (_isArray)
+            {
+                foreach (var o in obj.Properties())
+                {
+                    if (null == (o.Value as JArray))
+                    {
+                        obj[o.Name] = new JArray() { o.Value };
+                    }
+                }
+            }
+            isArray = _isArray;
+            return obj;
+        }
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个JObject中, {k->[],k->[],...}
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="indexFieldName">用做索引的字段名</param>
+        /// <param name="excludeFields">排除的字段名列表</param>
+        /// <returns>{}</returns>
+        public JObject ReadTabelToJObjectWithArrayValues(string tableName, string indexFieldName, params string[] excludeFields)
+        {
+            Assert(HasFiledInTable(tableName, indexFieldName),
+                Format("在表[{0}]中未找到key:[{1}]", tableName, indexFieldName));
+
+            var ex = new List<string>(excludeFields);
+            //ex.Add(indexFieldName);
+            var obj = new JObject();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var jobj = new JObject();
+                    var id = "";
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == indexFieldName)
+                        {
+                            id = rdr.GetValue(i).ToString();
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            object v = rdr.GetValue(i);
+
+                            if (DBNull.Value == v)
+                            {
+                                var t = rdr.GetFieldType(i).Name;
+                                switch (t)
+                                {
+                                    case "Int32":
+                                        v = 0;
+                                        break;
+                                    case "String":
+                                        v = "";
+                                        break;
+                                    case "Single":
+                                        v = 0;
+                                        break;
+                                }
+                            }
+                            jobj.Add(rdr.GetName(i), JToken.FromObject(v));
+                        }
+                    }
+
+                    var arr = obj[id] as JArray;
+                    if (null != arr)
+                    {
+                        arr.Add(jobj);
+                    }
+                    else
+                    {
+                        obj[id] = new JArray() { jobj };
+                    }
+
+                }
+
+            });
+
+            return obj;
+        }
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个简单的key=>value结构中. 即只取表中的两个字段
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="keyName">用作属性名称的字段</param>
+        /// <param name="valueName">用作值的字段</param>
+        /// <returns>{k:v,k:v,...}</returns>
+        public JObject ReadTabelToSimpleJObject(string tableName, string keyName, string valueName)
+        {
+            Assert(HasFiledInTable(tableName, keyName),
+                    Format("在表[{0}]中未找到key:[{1}]", tableName, keyName));
+            Assert(HasFiledInTable(tableName, valueName),
+                Format("在表[{0}]中未找到字段:[{1}]", tableName, valueName));
+            var jobj = new JObject();
+
+            ReadTabel(tableName, rdr =>
+           {
+               while (rdr.Read())
+               {
+                   var key = "";
+                   Object value = null;
+                   for (int i = 0; i < rdr.FieldCount; i++)
+                   {
+                       if (rdr.GetName(i) == keyName)
+                       {
+                           key = rdr.GetValue(i).ToString();
+                       }
+                       if (rdr.GetName(i) == valueName)
+                       {
+                           value = rdr.GetValue(i);
+                       }
+                   }
+                   jobj.Add(key, JToken.FromObject(value));
+               }
+           });
+
+            return jobj;
+        }
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个由两个字段内容拼接成的字符串作为索引的JObject中
+        /// </summary>
+        /// <param name="tableName">表名称</param>
+        /// <param name="firstFieldName">第一个字段</param>
+        /// <param name="secondFieldName">第二个字段</param>
+        /// <param name="excludeFields">排除的字段名...</param>
+        /// <returns> {field(1)-field(2):{},...} </returns>
+        public JObject ReadTabelToJObjectWithCombinedIndex(string tableName, string firstFieldName, string secondFieldName, out bool isArray, params string[] excludeFields)
+        {
+            Assert(HasFiledInTable(tableName, firstFieldName),
+                 Format("在表[{0}]中未找到字段:[{1}]", tableName, firstFieldName));
+            Assert(HasFiledInTable(tableName, secondFieldName),
+                Format("在表[{0}]中未找到字段:[{1}]", tableName, secondFieldName));
+            bool _isArray = false;
+            var ex = new List<string>(excludeFields);
+            //ex.Add(firstFieldName);
+            //ex.Add(secondFieldName);
+            var obj = new JObject();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var jobj = new JObject();
+                    var firstId = "";
+                    var secondId = "";
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == firstFieldName)
+                        {
+                            firstId = rdr.GetValue(i).ToString();
+                        }
+                        if (rdr.GetName(i) == secondFieldName)
+                        {
+                            secondId = rdr.GetValue(i).ToString();
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            Object v = rdr.GetValue(i);
+                            if (DBNull.Value == v)
+                            {
+                                var t = rdr.GetFieldType(i).Name;
+                                switch (t)
+                                {
+                                    case "Int32":
+                                        v = 0;
+                                        break;
+                                    case "String":
+                                        v = "";
+                                        break;
+                                    case "Single":
+                                        v = 0;
+                                        break;
+                                }
+                            }
+                            jobj.Add(rdr.GetName(i), JToken.FromObject(v));
+                        }
+                    }
+                    var key = firstId + "-" + secondId;
+                    if (obj[key] == null)
+                    {  // 防止重复
+                        obj.Add(key, jobj);
+                    }
+                    else
+                    {  // 已有对象 => 智能转换多值对应为[]
+                        _isArray = true;
+                        var arr = obj[key] as JArray;
+                        if (arr != null)
+                        { // 已经是jarray
+                            arr.Add(jobj);
+                        }
+                        else
+                        {    // 创建Jarray
+                            obj[key] = new JArray() { obj[key], jobj };
+                        }
+                    }
+                }
+            });
+
+            if (_isArray)
+            {
+                foreach (var o in obj.Properties())
+                {
+                    if (null == (o.Value as JArray))
+                    {
+                        obj[o.Name] = new JArray() { o.Value };
+                    }
+                }
+            }
+            isArray = _isArray;
+            return obj;
+        }
+
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个{key11:{key1_21:{},key1_22:{},...},key12:{key2_21:{},key2_22:{},...},...}结构数据中
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="indexFieldName">用做索引的字段名</param>
+        /// <param name="secondaryIndexName">用做次级索引的字段名</param>
+        /// <param name="excludeFields">排除的字段名列表</param>
+        /// <returns>拥有二级索引的json对象,二级节点是{}</returns>
+        public JObject ReadTabelToJObjectWithSecondaryIndex(string tableName,
+            string indexFieldName, string secondaryIndexName, out bool isArray, params string[] excludeFields)
+        {
+            Assert(HasFiledInTable(tableName, indexFieldName),
+            Format("在表[{0}]中未找到字段:[{1}]", tableName, indexFieldName));
+            Assert(HasFiledInTable(tableName, secondaryIndexName),
+                Format("在表[{0}]中未找到字段:[{1}]", tableName, secondaryIndexName));
+            var _isArray = false;
+            var ex = new List<string>(excludeFields);
+            //ex.Add(indexFieldName);
+            //ex.Add(secondaryIndexName);
+            var obj = new JObject();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var jobj = new JObject();
+                    var id = "";
+                    var secondId = "";
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == indexFieldName)
+                        {
+                            id = rdr.GetValue(i).ToString();
+                        }
+                        if (rdr.GetName(i) == secondaryIndexName)
+                        {
+                            secondId = rdr.GetValue(i).ToString();
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            Object v = rdr.GetValue(i);
+                            if (DBNull.Value == v)
+                            {
+                                var t = rdr.GetFieldType(i).Name;
+                                switch (t)
+                                {
+                                    case "Int32":
+                                        v = 0;
+                                        break;
+                                    case "String":
+                                        v = "";
+                                        break;
+                                    case "Single":
+                                        v = 0;
+                                        break;
+                                }
+                            }
+                            jobj.Add(rdr.GetName(i), JToken.FromObject(v));
+                        }
+                    }
+
+                    if (obj[id] == null)
+                    {  // 没有建立一级对象
+                        var o = new JObject();
+                        o.Add(secondId, jobj);
+                        obj.Add(id, o);
+                    }
+                    else
+                    {  // 已有一级对象
+                        var o = obj[id] as JObject;  // 一级对象
+                        if (o[secondId] == null)
+                        {  // 添加新值
+                            o.Add(secondId, jobj);
+                        }
+                        else
+                        {    // 已有对象 => 智能转换多值对应为[]
+                            _isArray = true;
+                            var arr = o[secondId] as JArray;
+                            if (arr != null)
+                            { // 已经是jarray
+                                arr.Add(jobj);
+                            }
+                            else
+                            {    // 创建Jarray
+                                o[secondId] = new JArray() { o[secondId], jobj };
+                            }
+                        }
+                    }
+                }
+            });
+            if (_isArray)
+            {
+                foreach (var o in obj.Properties())
+                {
+                    foreach (var o2 in (o.Value as JObject).Properties())
+                    {
+                        if (!(obj[o.Name][o2.Name] is JArray))
+                        {
+                            obj[o.Name][o2.Name] = new JArray() { obj[o.Name][o2.Name] };
+                        }
+                    }
+                }
+            }
+            isArray = _isArray;
+            return obj;
+        }
+
+
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个行数组[{},{}]
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="excludeFields">排除的字段名列表</param>
+        /// <returns>普通行数组[{},{},...]</returns>
+        public Dictionary<string, object>[] ReadTabelToArray(string tableName, params string[] excludeFields)
+        {
+            var ex = new List<string>(excludeFields);
+            var arr = new List<Dictionary<string, object>>();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var r = new Dictionary<string, object>(rdr.FieldCount - 1);
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            r.Add(rdr.GetName(i), rdr.GetValue(i));
+                        }
+                    }
+                    arr.Add(r);
+                }
+            });
+            return arr.ToArray();
+        }
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个dic中 {"key":{}}
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="indexFieldName">仅限索引字段(1对多的情况暂不支持)</param>
+        /// <param name="excludeFields">排除的字段名列表</param>
+        /// <typeparam name="T">string, number</typeparam>
+        /// <returns>嵌套的dictionary:{k:{k,v},k:{k:v},...}</returns>
+        public Dictionary<T, Dictionary<string, object>> ReadTabelToDic_Dic<T>(string tableName, string indexFieldName, params string[] excludeFields)
+        {
+            Assert(HasFiledInTable(tableName, indexFieldName),
+                Format("在表[{0}]中未找到字段:[{1}]", tableName, indexFieldName));
+            var ex = new List<string>(excludeFields);
+            //ex.Add(indexFieldName);
+            var dic = new Dictionary<T, Dictionary<string, object>>();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var r = new Dictionary<string, object>(rdr.FieldCount - 1);
+                    var t = default(T);
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == indexFieldName)
+                        {
+                            t = (T)rdr.GetValue(i);
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            r.Add(rdr.GetName(i), rdr.GetValue(i));
+                        }
+                    }
+                    dic.Add(t, r);
+                }
+
+            });
+            return dic;
+        }
+
+        /// <summary>
+        /// 读取数据库表 -> 到一个dic中 {"key":[]}
+        /// </summary>
+        /// <param name="tableName">表名</param>
+        /// <param name="indexFieldName">索引字段名</param>
+        /// <param name="excludeFields">排除的字段名列表</param>
+        /// <typeparam name="T">string, number</typeparam>
+        /// <returns>复杂嵌套dictionary:{k:[{},{},..],k:[{},{},..]..}</returns>
+        public Dictionary<T, List<Dictionary<string, object>>> ReadTabelToDic_List<T>(string tableName, string indexFieldName, params string[] excludeFields)
+        {
+            Assert(HasFiledInTable(tableName, indexFieldName),
+                Format("在表[{0}]中未找到字段:[{1}]", tableName, indexFieldName));
+            var ex = new List<string>(excludeFields);
+            //ex.Add(indexFieldName);
+            var dic = new Dictionary<T, List<Dictionary<string, object>>>();
+            ReadTabel(tableName, rdr =>
+            {
+                while (rdr.Read())
+                {
+                    var r = new Dictionary<string, object>(rdr.FieldCount - 1);
+                    var t = default(T);
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == indexFieldName)
+                        {
+                            t = (T)rdr.GetValue(i);
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            r.Add(rdr.GetName(i), rdr.GetValue(i));
+                        }
+                    }
+                    if (dic.ContainsKey(t))
+                    {
+                        dic[t].Add(r);
+                    }
+                    else
+                    {
+                        dic.Add(t, new List<Dictionary<string, object>> { r });
+                    }
+                }
+            });
+            return dic;
+        }
+
+        #endregion
+
+        #region `   数据写   `
+
+        /// <summary>
+        /// 执行非查询类的SQL语句,比如插入/更新/删除之类的.
+        /// </summary>
+        /// <param name="sql"></param>
+        /// <returns></returns>
+        public int ExecuteSqlNonQuery(string sql) {
+            using var cmd = new MySqlCommand(sql, conn);
+            return cmd.ExecuteNonQuery();
+        }
+
+
+
+        #endregion
+
+        #region '    辅助方法    '
+
+        /// <summary>
+        /// 查询某表中是否存在指定字段
+        /// </summary>
+        /// <param name="tableName"></param>
+        /// <param name="fieldName"></param>
+        /// <returns></returns>
+        public bool HasFiledInTable(string tableName, string fieldName)
+        {
+            Trace.Assert(TableExist(tableName), tableName + " 未找到");
+            return GetTableFieldsInfo(tableName).ContainsKey(fieldName);
+        }
+
+        /// <summary>
+        /// 检查指定的表格是否存在
+        /// </summary>
+        /// <param name="tableName"></param>
+        /// <returns></returns>
+        public bool TableExist(string tableName)
+        {
+            try
+            {
+                new MySqlCommand("select * from " + tableName + " limit 1;", conn).ExecuteNonQuery();
+            }
+            catch (MySqlException ex)
+            {
+                switch (ex.Number)
+                {
+                    case 1146:
+                        return false;
+                    default:
+                        Debug.WriteLine(ex.Message);
+                        return false;
+                }
+            }
+            return true;
+        }
+
+        /// <summary>
+        /// 执行sql
+        /// </summary>
+        /// <param name="sql"></param>
+        public void Query(string sql)
+        {
+            try
+            {
+                using (var cmd = new MySqlCommand(sql, conn))
+                {
+                    cmd.ExecuteNonQuery();
+                }
+            }
+            catch (MySqlException ex)
+            {
+                Debug.WriteLine(ex.Message);
+            }
+        }
+        /// <summary>
+        /// 执行sql 查询
+        /// </summary>
+        /// <param name="sqlcmd"></param>
+        /// <param name="working"></param>
+        public void QueryTable(string sqlcmd, Action<MySqlDataReader> working)
+        {
+            try
+            {
+                using (var cmd = new MySqlCommand(sqlcmd, conn))
+                using (MySqlDataReader rdr = cmd.ExecuteReader())
+                {
+
+                    if (null != working)
+                    {
+                        working.Invoke(rdr);
+                    }
+                }
+            }
+            catch (MySqlException ex)
+            {
+                Debug.WriteLine(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 查询表格的字段信息  FieldName :
+        ///     <Type(字段类型 in .net)>
+        ///     <Collation(字符集及排序)>
+        ///     <Null(是否可空)>
+        ///     <Key(索引类型)>
+        ///     <Default(默认值)>
+        ///     <Extra(自增)>
+        ///     <Privileges(权限)>
+        ///     <Comment(注释)>
+        /// </summary>
+        /// <param name="tableName"></param>
+        /// <returns></returns>
+        public Dictionary<string, Dictionary<string, object>> GetTableFieldsInfo(string tableName)
+        {
+            var ex = new List<string>();
+            var dic = new Dictionary<string, Dictionary<string, object>>();
+            QueryTable(string.Format("show full fields from {0}", tableName), rdr =>
+            {
+                while (rdr.Read())
+                {
+
+                    var r = new Dictionary<string, object>();
+                    var fieldName = rdr.GetString(0);
+                    for (int i = 0; i < rdr.FieldCount; i++)
+                    {
+                        if (rdr.GetName(i) == "Field")
+                        {
+                            fieldName = rdr.GetString(i);
+                        }
+                        if (!ex.Contains(rdr.GetName(i)))
+                        {
+                            r.Add(rdr.GetName(i), rdr.GetValue(i));
+                        }
+
+                    }
+                    dic.Add(fieldName, r);
+                }
+            });
+            ReadTabel(tableName, rdr =>               // 用。net类型替换掉myslq 类型字符串
+            {
+                for (var i = 0; i < rdr.FieldCount; i++)
+                {
+
+                    var filedName = rdr.GetName(i);               // 字段名
+                    var filedType = rdr.GetFieldType(i).Name;     // 字段类型
+                    if (dic.ContainsKey(filedName))
+                    {
+                        dic[filedName][nameof(EFieldInfo.Type)] = filedType;
+                    }
+                }
+            });
+            return dic;
+        }
+
+        /// <summary>
+        /// 查询表格的最后修改时间
+        /// </summary>
+        /// <param name="tableName"></param>
+        /// <returns>最后修改时间或者创建时间, 异常: 返回当前时间</returns>
+        public DateTime TableLastModified(string tableName)
+        {
+            var sql = string.Format("SELECT UPDATE_TIME, CREATE_TIME " +
+                      "FROM information_schema.tables " +
+                      "where TABLE_SCHEMA='{0}' and TABLE_NAME='{1}'",
+                          this.conn.Database, tableName);
+            using (var cmd = new MySqlCommand(sql, conn))
+            using (MySqlDataReader rdr = cmd.ExecuteReader())
+            {
+                if (rdr.Read())
+                {  // 上次更新时间为null的情况下使用表的创建时间,都找不到值的情况下返回当前时间   
+                    return rdr.IsDBNull(0) ? (rdr.IsDBNull(1) ? DateTime.Now : rdr.GetDateTime(1)) : rdr.GetDateTime(0);
+                }
+            }
+            throw new Exception("没有找到对应的表");
+        }
+        #endregion
+
+
+
+    }
+}

+ 31 - 0
CSserver/csserver/db/Redis.cs

@@ -0,0 +1,31 @@
+using StackExchange.Redis;
+
+namespace Chat
+{
+    class Redis
+    {
+        private static ConnectionMultiplexer redis;
+
+        static public ConnectionMultiplexer Ins
+        {
+            get
+            {
+                if (null == redis)
+                {
+                    redis = ConnectionMultiplexer.Connect(Config.Ins.redis);
+                }
+                return redis;
+            }
+        }
+        /// <summary>
+        /// 获取操作redis数据库的接口对象
+        /// </summary>
+        /// <param name="id">指定db的编号</param>
+        /// <returns></returns>
+        public static IDatabase Rdb(int id = 0)
+        {
+            return Ins.GetDatabase(id);
+        }
+
+    }
+}

+ 197 - 0
CSserver/csserver/server/Peer.cs

@@ -0,0 +1,197 @@
+using ProtoDataBuff;
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Net.Sockets;
+using System.Threading.Channels;
+using System.Threading.Tasks;
+using Chat;
+using pb = global::Google.Protobuf;
+namespace ChatServer.server
+{
+    /// <summary>
+    /// 客户端状态
+    /// </summary>
+    enum ClientState
+    {
+        /// <summary>
+        /// 已连接,未登录
+        /// </summary>
+        Connected,
+        /// <summary>
+        /// 已登录
+        /// </summary>
+        Logined,
+        /// <summary>
+        /// 已离开
+        /// </summary>
+        Leaved
+    }
+    class Peer
+    {
+        /// <summary>
+        /// 客户端状态字段
+        /// </summary>
+        public ClientState CurrentState = ClientState.Connected;
+
+        /// <summary>
+        /// peer的唯一Id
+        /// </summary>
+        public readonly int Id;
+
+        /// <summary>
+        /// socket连接
+        /// </summary>
+        public readonly Socket Sock;
+        /// <summary>
+        /// 玩家ID
+        /// </summary>
+        public string UID { get; private set; } = "";
+        /// <summary>
+        /// 分区id
+        /// </summary>
+        public int zoneid { get; private set; } = 0;
+        public string Name { get; private set; } = "";
+        /// <summary>
+        /// 线程安全的peer计数器
+        /// </summary>
+        private volatile static int _UniqPeerId = 0;
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="sock"></param>
+        public Peer(Socket sock)
+        {
+            this.Sock = sock;
+            Sock.ReceiveTimeout = 1500;                                         // 接收等待超时时间设为1.5秒 
+            Id = _UniqPeerId++;
+            var t = Task.Run(() => recv(Sock));                                 // 新建接收线程 
+            var tcs = Task.Run(WriteToClient);                                  // 向客户端发送消息线程 
+        }
+
+        public string Key => UID + ":" + zoneid;
+        public void OnLogin(sSocketData data)
+        {
+            var msg = CS_ChatLogin.Parser.ParseFrom(data._data);
+            if (msg != null)
+            {
+                this.UID = msg.Uid;
+                this.zoneid = msg.Zoneid;
+                this.Name = msg.Name;
+                Program.AddPeer(this);
+                this.SendEvent(eProtocalCommand.ScChatLogin, new SC_ChatLogin() { Code = SC_ChatLogin.Types.ErrorCode.Ok });
+            }
+        }
+        public void Close()
+        {
+            this.CurrentState = ClientState.Leaved;                              // 关闭接收窗口
+            Program.RemovePeer(this.Key);                                      // 从房间中移除自己 
+            this.sendDataBuffer.Reader.Completion
+                .ContinueWith(t =>
+                Task.Delay(5000).ContinueWith(t1 => Sock.Close())
+                );                                                               // 延迟5秒关闭套接字
+        }
+
+
+        /// <summary>
+        /// 向客户端发送事件
+        /// </summary>
+        public void SendEvent(eProtocalCommand msgType, pb::IMessage msg)
+        {
+            SendToClient(msgType, msg);
+        }
+
+        /// <summary>
+        /// 向客户端发送消息
+        /// </summary>
+        /// <param name="msgType"></param>
+        /// <param name="msg"></param>
+        void SendToClient(eProtocalCommand msgType, pb::IMessage msg)
+        {
+            using var ms = new MemoryStream();
+            using var os = new pb::CodedOutputStream(ms);
+            msg.WriteTo(os);
+            os.Flush();
+            ms.Seek(0, SeekOrigin.Begin);
+            var sdata = sSocketData.FromBytes(msgType, ms.ToArray());
+            sendDataBuffer.Writer.WriteAsync(sdata).AsTask().Wait();
+        }
+
+
+        /// <summary>
+        /// 向客户端写入消息
+        /// </summary>
+        async void WriteToClient()
+        {
+            while (true)
+            {
+                try
+                {
+                    var msg = await sendDataBuffer.Reader.ReadAsync();
+                    await Sock.SendAsync(new ArraySegment<byte>(msg.ToBytes()), SocketFlags.None);
+                }
+                catch (Exception)
+                {
+                    break;
+                }
+
+            }
+            Close();
+        }
+
+
+        /// <summary>
+        /// 发送buffer
+        /// </summary>
+        private Channel<sSocketData> sendDataBuffer = Channel.CreateUnbounded<sSocketData>();
+
+
+        /// <summary>
+        /// 接收客户端发来的信息,客户端套接字对象
+        /// </summary>
+        /// <param name="socketclientpara"></param>    
+        async void recv(Socket socketServer)
+        {
+
+            var _databuffer = new DataBuffer();
+            byte[] arrServerRecMsg = new byte[4096];                                       // 创建一个内存缓冲区,其大小为4k字节  
+            while (true)
+            {
+                try
+                {
+                    var length = await socketServer.ReceiveAsync(new ArraySegment<byte>(arrServerRecMsg), SocketFlags.None);   // 将接收到的信息存入到内存缓冲区,并返回其字节数组的长度    
+                    if (length <= 0 || CurrentState == ClientState.Leaved)                                                     // 视为客户端已经close连接
+                    {
+                        break;
+                    }
+                    _databuffer.AddBuffer(arrServerRecMsg, length);                                  //将收到的数据添加到缓存器中
+                    while (_databuffer.GetData(out sSocketData _socketData))                         //取出一条完整数据
+                    {
+                        if (eProtocalCommand.CsChatLogin == _socketData._protocallType)
+                        {
+                            OnLogin(_socketData);
+                            continue;
+                        }
+                        await Program.MsgChannel.Writer.WriteAsync(new KeyValuePair<string, sSocketData>(this.Key, _socketData));    // 放入channel
+                    }
+                    Array.Clear(arrServerRecMsg, 0, length);
+                }
+                catch (SocketException e)
+                {
+                    if (e.ErrorCode == 10060)                                          // 超时的时候错误号码是10060
+                    {
+                        continue;                                                      // 继续等待  
+                    }
+                    break;
+                }
+                catch (Exception)
+                {
+                    break;
+                }
+            }
+
+            Close();                                                                  // 关闭
+        }
+
+    }
+}