直接用Socket TCP开发网络游戏(二)

来源:互联网 发布:wow猎人 知乎 编辑:程序博客网 时间:2024/06/05 14:06

01,如何解决SQL的注入问题

用这种方法的话就不会出现那种恶意输入的情况,代码实现如下:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using MySql.Data.MySqlClient;namespace MySql数据库操作{    class Program    {        static void Main(string[] args)        {            string connStr = "Database=test007;Data Source=127.0.0.1;port=3306;User Id=root;Password=root";            MySqlConnection conn = new MySqlConnection(connStr);            conn.Open();            #region 查询            #region 读取多条            //MySqlCommand cmd = new MySqlCommand("select * from user ", conn);            #endregion            #region 读取一条            //  MySqlCommand cmd = new MySqlCommand("select * from user where id = 1", conn);            #endregion            //MySqlDataReader reader = cmd.ExecuteReader();            #region 读取一条            // if (reader.HasRows) //得到一个值指示是否MySqlDataReader包含一个或多个行。            // {            //reader.Read(); //表示读取一条消息,多次读取多次调用            //string username = reader.GetString("username");            //string passwore = reader.GetString("password");            //Console.WriteLine(username + " " + passwore);            //  }            #endregion            #region 读取多条            //while (reader.Read())            //{            //    string username = reader.GetString("username");            //    string passwore = reader.GetString("password");            //    Console.WriteLine(username + " " + passwore);            //}            #endregion            //reader.Close();            #endregion            #region 插入            string username = "zain"; string password = "lcker; delete form user";  ///用户组拼 有bug 用户可以恶意输入sql语句 下节课再解决            MySqlCommand cmd = new MySqlCommand("insert into user set username=@un, password=@pwd", conn);            cmd.Parameters.AddWithValue("un", username);  //添加参数 。。根据字符查找 cmd中的 字符 并替换成vaule            cmd.Parameters.AddWithValue("pwd", password);            cmd.ExecuteNonQuery();//执行插入  并返回插入的行数                       #endregion            conn.Close();            Console.ReadKey();        }    }}

02,数据库数据的更新和删除


using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using MySql.Data.MySqlClient;namespace MySql数据库操作{    class Program    {        static void Main(string[] args)        {            string connStr = "Database=test007;Data Source=127.0.0.1;port=3306;User Id=root;Password=root";            MySqlConnection conn = new MySqlConnection(connStr);            conn.Open();            #region 查询            #region 读取多条            //MySqlCommand cmd = new MySqlCommand("select * from user ", conn);            #endregion            #region 读取一条            //  MySqlCommand cmd = new MySqlCommand("select * from user where id = 1", conn);            #endregion            //MySqlDataReader reader = cmd.ExecuteReader();            #region 读取一条            // if (reader.HasRows) //得到一个值指示是否MySqlDataReader包含一个或多个行。            // {            //reader.Read(); //表示读取一条消息,多次读取多次调用            //string username = reader.GetString("username");            //string passwore = reader.GetString("password");            //Console.WriteLine(username + " " + passwore);            //  }            #endregion            #region 读取多条            //while (reader.Read())            //{            //    string username = reader.GetString("username");            //    string passwore = reader.GetString("password");            //    Console.WriteLine(username + " " + passwore);            //}            #endregion            //reader.Close();            #endregion            #region 插入            //string username = "zain"; string password = "lcker; delete form user";  ///用户组拼 有bug 用户可以恶意输入sql语句 下节课再解决            //MySqlCommand cmd = new MySqlCommand("insert into user set username=@un, password=@pwd", conn);            //cmd.Parameters.AddWithValue("un", username);  //根据字符查找 cmd中的 字符 并替换成vaule            //cmd.Parameters.AddWithValue("pwd", password);            //cmd.ExecuteNonQuery();//执行插入  并返回插入的行数            ////Console.WriteLine(cmd.ExecuteNonQuery());             #endregion            #region 删除            //MySqlCommand cmd = new MySqlCommand("delete from user where id =@id", conn);            //cmd.Parameters.AddWithValue("id", 2);            //cmd.ExecuteNonQuery(); //执行            #endregion            #region 更新            MySqlCommand cmd = new MySqlCommand("update  user set password=@pwd where id=3", conn);            cmd.Parameters.AddWithValue("pwd", "a");            cmd.ExecuteNonQuery(); //执行            #endregion            conn.Close();            Console.ReadKey();        }    }}

03,服务器端分成架构

    Server :             用来创建Socket 监听客户端的连接
    ConnHelper:  工具类 用来连接数据库 建立MySqlConnection
    Controller:      处理客户端的请求,客户端的请求发送到server端 ,server会调用相应的controller进行处理
    Model:            是和数据库中的表是对应着的  一个Model类对应一个数据库表
    DAO :            用来操作数据库的

04,学习小提示和项目的目录结构的创建

架构的话大概就是这样

05,创建Server类 开启接收客户端的连接

代码如下:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;namespace GameServer.Server{    class Server    {        private IPEndPoint ipEndPoint;        private Socket serverSocket;        public Server()        {        }        public Server(string IpStr, int port)        {            SetIpAndPort(IpStr, port);        }        public void  SetIpAndPort(string IpStr, int port)        {            ipEndPoint = new IPEndPoint(IPAddress.Parse(IpStr), port);        }        public void Start()        {             serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            serverSocket.Bind(ipEndPoint);            serverSocket.Listen(0);            serverSocket.BeginAccept(AcceptCallBack, null);        }                public void AcceptCallBack(IAsyncResult ar)        {            Socket clientSocket = serverSocket.EndAccept(ar);        }    }}

06,创建Client类 处理跟客户端的数据通信

准备用message做桥梁: 创建客户端 服务器也需要改一下代码

客户端代码实现如下:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;namespace GameServer.Server{    class Client    {        private Socket clientSocket;        private Server server;        public Client()        {        }        public Client(Socket clientSocket, Server server)        {            this.clientSocket = clientSocket;            this.server = server;        }        public void Start()        {            clientSocket.BeginReceive(null, 0, 0,SocketFlags.None,AsyncCallback,null);        }        public void AsyncCallback(IAsyncResult ar){            try            {                int count = clientSocket.EndReceive(ar);                if (count == 0)                {                    Close();                }                clientSocket.BeginReceive(null, 0, 0, SocketFlags.None, AsyncCallback, null);                //TODO 处理接收到的数据            }            catch (Exception e)            {                Console.WriteLine(e);                Close();            }                    }        private void Close()        {            if (clientSocket!=null)                        clientSocket.Close();            server.RemoveClient(this);        }    }}

服务器代码如下:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;namespace GameServer.Server{    class Server    {        private IPEndPoint ipEndPoint;        private Socket serverSocket;        private List<Client> clientList;        public Server()        {        }        public Server(string IpStr, int port)        {            SetIpAndPort(IpStr, port);        }        public void  SetIpAndPort(string IpStr, int port)        {            ipEndPoint = new IPEndPoint(IPAddress.Parse(IpStr), port);        }        public void Start()        {             serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            serverSocket.Bind(ipEndPoint);            serverSocket.Listen(0);            serverSocket.BeginAccept(AcceptCallBack, null);        }                public void AcceptCallBack(IAsyncResult ar)        {            Socket clientSocket = serverSocket.EndAccept(ar);            Client client = new Client(clientSocket,this);            client.Start();            clientList.Add(client);//添加到list集合        }        public void RemoveClient(Client client)        {            lock (clientList) //锁定移除            {                clientList.Remove(client);            }                   }    }}

07,创建Message处理客户端的消息解析;

Message类代码实现如下:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace GameServer.Server{    class Message    {        private byte[] data = new byte[1024]; //这个数组用来存取我们读取到的数据,让消息的长度最少能存的下1024 ,如果说最大的消息数组存不下的话 就没办法完整的读取这条消息了        //如果消息不完整 我们不处理。我们会读取下一条        private int startIndex = 0;//标识位,表示现在数据存储在什么位置了 ,如果没数据 sartIndex从0开始,如果存了10个之后 这个startIndex就等于10;                                   //再来数据的话就从10开始存,同时也代表了存了多少个字节的数据。        /// <summary>        /// 这个数组用来存取我们读取到的数据        /// </summary>        public byte[] Data        {            get { return data; }        }        /// <summary>        /// 代表存储了多少个字节的数据  开始索引        /// </summary>        public int StartIndex        {            get { return startIndex; }        }        /// <summary>        /// 剩余的空间        /// </summary>        public int RemaniSize        {            get { return data.Length - startIndex; }        }        /// <summary>        /// 更新了多少数据        /// </summary>        /// <param name="count"></param>        //public void AddCount(int count)        //{        //    startIndex += count;        //}        /// <summary>        /// 解析数据或者叫做读取数据  newDataAmount数量        /// </summary>        public void ReadMessage(int newDataAmount)        {            startIndex += newDataAmount;            while (true)            {                if (startIndex <= 4) return;                int count = BitConverter.ToInt32(data, 0);//这个只占前四个字节  ,变成int 就是数据长度                if ((startIndex - 4) >= count) //(startIndex-4)是剩余数据的长度 ,大于count 就说明数据是完整的                {                    string s = Encoding.UTF8.GetString(data, 4, count);                    Console.WriteLine("解析出来一条数据 :" + s);                    Array.Copy(data, count + 4, data, 0, startIndex - 4 - count);                    startIndex -= (count + 4); //移动完了之后更新startIndex                }                else                {                    break;                }            }                  }          }}

Client类代码稍微做了修改:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;namespace GameServer.Server{    class Client    {        private Socket clientSocket;        private Server server;        private Message messgae = new Message();        public Client()        {        }        public Client(Socket clientSocket, Server server)        {            this.clientSocket = clientSocket;            this.server = server;        }        public void Start()        {            clientSocket.BeginReceive(messgae.Data, messgae.StartIndex, messgae.RemaniSize,SocketFlags.None,AsyncCallback,null);        }        public void AsyncCallback(IAsyncResult ar){            try            {                int count = clientSocket.EndReceive(ar);                if (count == 0)                {                    Close();                }                messgae.ReadMessage(count);                Start();                //TODO 处理接收到的数据            }            catch (Exception e)            {                Console.WriteLine(e);                Close();            }        }        private void Close()        {            if (clientSocket!=null)                        clientSocket.Close();            server.RemoveClient(this);        }    }}

服务器类没做变化:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;namespace GameServer.Server{    class Server    {        private IPEndPoint ipEndPoint;        private Socket serverSocket;        private List<Client> clientList;        public Server()        {        }        public Server(string IpStr, int port)        {            SetIpAndPort(IpStr, port);        }        public void  SetIpAndPort(string IpStr, int port)        {            ipEndPoint = new IPEndPoint(IPAddress.Parse(IpStr), port);        }        public void Start()        {            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            serverSocket.Bind(ipEndPoint);            serverSocket.Listen(0);            serverSocket.BeginAccept(AcceptCallBack, null);        }                public void AcceptCallBack(IAsyncResult ar)        {            Socket clientSocket = serverSocket.EndAccept(ar); //            Client client = new Client(clientSocket,this);            client.Start();            clientList.Add(client); //添加到list集合        }        public void RemoveClient(Client client)        {            lock (clientList) //锁定移除            {                clientList.Remove(client);            }                   }    }}

08,开发Controller控制层

代码如下:


然后 再新建一个类库 Common 新建两个类 每个类都改为枚举类型 分别为ActionCode,RequesCode。

using System;using System.Collections.Generic;using System.Text;namespace Common{    public enum ActionCode    {        None,    }}

using System;using System.Collections.Generic;using System.Text;namespace Common{    public  enum RequesCode    {        None,    }}

在Controller文件夹下创建一个新的类 抽象类 BaseController   把刚才的类库添加引用到BaseController,和命名空间

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Common;namespace GameServer.Controller{    abstract  class BaseController    {        RequesCode requesCode = RequesCode.None;        public virtual void DefaultHandle() { }    }}

09,客户端和服务器端的请求发起处理流程


10,创建ControllerMessager管理所有的控制器


Clinet 直接和controllerManager直接打交道的话 耦合性太高,所以要通过中介(Server)来打交道;ControllerManager只让Server使用,其他都跟server交互;server就相当于一个中介

 服务器端要持有一个ControllerMessager 

ControllerManager 代码:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Common;namespace GameServer.Controller{    /// <summary>    /// 用来管理当前服务器端有哪些COntroller    /// </summary>    class ControllerManager    {        private Dictionary<RequesCode, BaseController> controllerDic = new Dictionary<RequesCode, BaseController>();        public ControllerManager()        {            Init();        }        /// <summary>        /// 初始化所有的Controller        /// </summary>        void Init()        {        }    }}

 


Server服务器代码:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;using GameServer.Controller;namespace GameServer.Server{    class Server    {        private IPEndPoint ipEndPoint;        private Socket serverSocket;        private List<Client> clientList;        private ControllerManager controllerManager = new ControllerManager();        public Server()        {        }        public Server(string IpStr, int port)        {            SetIpAndPort(IpStr, port);        }        public void  SetIpAndPort(string IpStr, int port)        {            ipEndPoint = new IPEndPoint(IPAddress.Parse(IpStr), port);        }        public void Start()        {            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            serverSocket.Bind(ipEndPoint);            serverSocket.Listen(0);            serverSocket.BeginAccept(AcceptCallBack, null);        }                public void AcceptCallBack(IAsyncResult ar)        {            Socket clientSocket = serverSocket.EndAccept(ar); //            Client client = new Client(clientSocket,this);            client.Start();            clientList.Add(client); //添加到list集合        }        public void RemoveClient(Client client)        {            lock (clientList) //锁定移除            {                clientList.Remove(client);            }                   }    }}


11,通过ControllerManager进行请求的分发处理


先完善BaseController :

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Common;namespace GameServer.Controller{    abstract  class BaseController    {        RequesCode requesCode = RequesCode.None;        public RequesCode RequestCode        {            get { return requesCode; }        }        /// <summary>        /// 用来处理默认的请求 当我们没有指定ActionCode的时候会执行        /// </summary>data 是发送的数据        public virtual string  DefaultHandle(string data) { return null;  }    }}

修改ControllerManager的代码:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Common;using System.Reflection;namespace GameServer.Controller{    /// <summary>    /// 用来管理当前服务器端有哪些COntroller    /// </summary>    class ControllerManager    {        private Dictionary<RequesCode, BaseController> controllerDic = new Dictionary<RequesCode, BaseController>();        public ControllerManager()        {            Init();        }        /// <summary>        /// 初始化所有的Controller        /// </summary>        void Init()        {            //TODO            DefaultController defaultController = new DefaultController();            controllerDic.Add(defaultController.RequestCode, defaultController);        }        /// <summary>        /// 处理请求        /// </summary> 通过requesCode 找到controller ,然后再actionCode 找到里面的方法        public void HandelRequset(RequesCode requesCode,ActionCode actionCode,string data)        {            BaseController controller;       bool isGet=     controllerDic.TryGetValue(requesCode, out controller);            if (isGet==false)            {                Console.WriteLine("无法得到"+requesCode+"所对应的Controller,无法处理请求");return;            }            string methodName = Enum.GetName(typeof(ActionCode), actionCode);           MethodInfo mi =    controller.GetType().GetMethod(methodName);            if (mi==null)            {                Console.WriteLine("[警告]在controller]"+controller.GetType()+"]中没有对应的处理方法"); return;            }            object[] parameters = new object[] {data };            ///o是是否要给客户端相应              object o=   mi.Invoke(controller, parameters); //调用controller方法 带data参数        }    }}

在controller文件夹新建DefaultController类:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace GameServer.Controller{    class DefaultController:BaseController  // 当RequesCode没有指定的时候调用    {    }}

结构图如下图

12,客户端请求相应的处理:

修改Server类 实例化出来一个ControllerManager:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;using GameServer.Controller;using Common;namespace GameServer.Servers{    class Server    {        private IPEndPoint ipEndPoint;        private Socket serverSocket;        private List<Client> clientList;        private ControllerManager controllerManager;        public Server()        {        }        public Server(string IpStr, int port)        {            controllerManager = new ControllerManager(this);            SetIpAndPort(IpStr, port);        }        public void  SetIpAndPort(string IpStr, int port)        {            ipEndPoint = new IPEndPoint(IPAddress.Parse(IpStr), port);        }        public void Start()        {            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            serverSocket.Bind(ipEndPoint);            serverSocket.Listen(0);            serverSocket.BeginAccept(AcceptCallBack, null);        }                public void AcceptCallBack(IAsyncResult ar)        {            Socket clientSocket = serverSocket.EndAccept(ar); //            Client client = new Client(clientSocket,this);            client.Start();            clientList.Add(client); //添加到list集合        }        public void RemoveClient(Client client)        {            lock (clientList) //锁定移除            {                clientList.Remove(client);            }                   }        /// <summary>        /// 给Clinet客户端发起相应        /// </summary>        /// <param name="clinet"></param>        /// <param name="requestCode"></param>        /// <param name="data"></param>        public  void SentResponse(Client clinet,RequesCode requestCode,string data)        {            //TODO给客户端相应        }    }}

Message类不做任何变化:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace GameServer.Servers{    class Message    {        private byte[] data = new byte[1024]; //这个数组用来存取我们读取到的数据,让消息的长度最少能存的下1024 ,如果说最大的消息数组存不下的话 就没办法完整的读取这条消息了        //如果消息不完整 我们不处理。我们会读取下一条        private int startIndex = 0;//标识位,表示现在数据存储在什么位置了 ,如果没数据 sartIndex从0开始,如果存了10个之后 这个startIndex就等于10;                                   //再来数据的话就从10开始存,同时也代表了存了多少个字节的数据。        /// <summary>        /// 这个数组用来存取我们读取到的数据        /// </summary>        public byte[] Data        {            get { return data; }        }        /// <summary>        /// 代表存储了多少个字节的数据  开始索引        /// </summary>        public int StartIndex        {            get { return startIndex; }        }        /// <summary>        /// 剩余的空间        /// </summary>        public int RemaniSize        {            get { return data.Length - startIndex; }        }        /// <summary>        /// 更新了多少数据        /// </summary>        /// <param name="count"></param>        //public void AddCount(int count)        //{        //    startIndex += count;        //}        /// <summary>        /// 解析数据或者叫做读取数据  newDataAmount数量        /// </summary>        public void ReadMessage(int newDataAmount)        {            startIndex += newDataAmount;            while (true)            {                if (startIndex <= 4) return;                int count = BitConverter.ToInt32(data, 0);//这个只占前四个字节  ,变成int 就是数据长度                if ((startIndex - 4) >= count) //(startIndex-4)是剩余数据的长度 ,大于count 就说明数据是完整的                {                    string s = Encoding.UTF8.GetString(data, 4, count);                    Console.WriteLine("解析出来一条数据 :" + s);                    Array.Copy(data, count + 4, data, 0, startIndex - 4 - count);                    startIndex -= (count + 4); //移动完了之后更新startIndex                }                else                {                    break;                }            }                  }          }}

client类不做变化:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;namespace GameServer.Servers{    class Client    {        private Socket clientSocket;        private Server server;        private Message messgae = new Message();        public Client()        {        }        public Client(Socket clientSocket, Server server)        {            this.clientSocket = clientSocket;            this.server = server;        }        public void Start()        {            clientSocket.BeginReceive(messgae.Data, messgae.StartIndex, messgae.RemaniSize,SocketFlags.None,AsyncCallback,null);        }        public void AsyncCallback(IAsyncResult ar){            try            {                int count = clientSocket.EndReceive(ar);                if (count == 0)                {                    Close();                }                messgae.ReadMessage(count);                Start();                //TODO 处理接收到的数据            }            catch (Exception e)            {                Console.WriteLine(e);                Close();            }        }        private void Close()        {            if (clientSocket!=null)                        clientSocket.Close();            server.RemoveClient(this);        }    }}

DefaultController类没变化:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace GameServer.Controller{    class DefaultController:BaseController  // 当RequesCode没有指定的时候调用    {    }}

ControllerManager类变化:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Common;using System.Reflection;using GameServer.Servers;namespace GameServer.Controller{    /// <summary>    /// 用来管理当前服务器端有哪些COntroller    /// </summary>    class ControllerManager    {        private Dictionary<RequesCode, BaseController> controllerDic = new Dictionary<RequesCode, BaseController>();        private Server server;        public ControllerManager(Server server)        {            this.server = server;            InitController();        }        /// <summary>        /// 初始化所有的Controller        /// </summary>        void InitController()        {            //TODO            DefaultController defaultController = new DefaultController();            controllerDic.Add(defaultController.RequestCode, defaultController);        }        /// <summary>        /// 处理请求        /// </summary> 通过requesCode 找到controller ,然后再actionCode 找到里面的方法        public void HandelRequset(RequesCode requesCode,ActionCode actionCode,string data ,Client client)        {            BaseController controller;       bool isGet=     controllerDic.TryGetValue(requesCode, out controller);            if (isGet==false)            {                Console.WriteLine("无法得到"+requesCode+"所对应的Controller,无法处理请求");return;            }            string methodName = Enum.GetName(typeof(ActionCode), actionCode);           MethodInfo mi =    controller.GetType().GetMethod(methodName);            if (mi==null)            {                Console.WriteLine("[警告]在controller]"+controller.GetType()+"]中没有对应的处理方法"); return;            }            object[] parameters = new object[] {data,client,server };            ///o是是否要给客户端相应              object o=   mi.Invoke(controller, parameters); //调用controller方法 带data参数            if (o==null||string.IsNullOrEmpty(o as string))            {                return;            }            server.SentResponse(client, requesCode, o as string);        }    }}

BaseController类也做了一些修改:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Common;using GameServer.Servers;namespace GameServer.Controller{    abstract  class BaseController    {        RequesCode requesCode = RequesCode.None;        public RequesCode RequestCode        {            get { return requesCode; }        }        /// <summary>        /// 用来处理默认的请求 当我们没有指定ActionCode的时候会执行        /// </summary>data 是发送的数据        public virtual string  DefaultHandle(string data,Client clinet ,Server server) { return null;  }    }}

Common类库也没变化 :

using System;using System.Collections.Generic;using System.Text;namespace Common{    public  enum RequesCode    {        None,    }}

using System;using System.Collections.Generic;using System.Text;namespace Common{    public enum ActionCode    {        None,    }

}

13,如何把客户端消息的解析和传递给ControllerManager进行处理

修改Message类:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Common;namespace GameServer.Servers{    class Message    {        private byte[] data = new byte[1024]; //这个数组用来存取我们读取到的数据,让消息的长度最少能存的下1024 ,如果说最大的消息数组存不下的话 就没办法完整的读取这条消息了        //如果消息不完整 我们不处理。我们会读取下一条        private int startIndex = 0;//标识位,表示现在数据存储在什么位置了 ,如果没数据 sartIndex从0开始,如果存了10个之后 这个startIndex就等于10;                                   //再来数据的话就从10开始存,同时也代表了存了多少个字节的数据。        /// <summary>        /// 这个数组用来存取我们读取到的数据        /// </summary>        public byte[] Data        {            get { return data; }        }        /// <summary>        /// 代表存储了多少个字节的数据  开始索引        /// </summary>        public int StartIndex        {            get { return startIndex; }        }        /// <summary>        /// 剩余的空间        /// </summary>        public int RemaniSize        {            get { return data.Length - startIndex; }        }        /// <summary>        /// 更新了多少数据        /// </summary>        /// <param name="count"></param>        //public void AddCount(int count)        //{        //    startIndex += count;        //}        /// <summary>        /// 解析数据或者叫做读取数据  newDataAmount数量        /// </summary>        public void ReadMessage(int newDataAmount,Action<RequesCode,ActionCode,string> processDataCallback)        {            startIndex += newDataAmount;            while (true)            {                if (startIndex <= 4) return;                int count = BitConverter.ToInt32(data, 0);//这个只占前四个字节  ,变成int 就是数据长度                if ((startIndex - 4) >= count) //(startIndex-4)是剩余数据的长度 ,大于count 就说明数据是完整的                {                    //string s = Encoding.UTF8.GetString(data, 4, count);                    //Console.WriteLine("解析出来一条数据 :" + s);                    RequesCode requestCode=(RequesCode)   BitConverter.ToInt32(data, 4); ///解析出来的是ResourceCode                    ActionCode actionCode = (ActionCode)BitConverter.ToInt32(data, 8); ///解析出来的是ActionCode                    string s = Encoding.UTF8.GetString(data, 12,count-8);                    processDataCallback(requestCode, actionCode, s);                    Array.Copy(data, count + 4, data, 0, startIndex - 4 - count);                    startIndex -= (count + 4); //移动完了之后更新startIndex                }                else                {                    break;                }            }                  }          }}
通过委托回调给Clinet,client回调给Server,server再回调给ControllerManager
Clinet:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;using Common;namespace GameServer.Servers{    class Client    {        private Socket clientSocket;        private Server server;        private Message messgae = new Message();        public Client()        {        }        public Client(Socket clientSocket, Server server)        {            this.clientSocket = clientSocket;            this.server = server;        }        public void Start()        {            clientSocket.BeginReceive(messgae.Data, messgae.StartIndex, messgae.RemaniSize,SocketFlags.None,AsyncCallback,null);        }        public void AsyncCallback(IAsyncResult ar){            try            {                int count = clientSocket.EndReceive(ar);                if (count == 0)                {                    Close();                }                messgae.ReadMessage(count,OnProcessMessage);                Start();                //TODO 处理接收到的数据            }            catch (Exception e)            {                Console.WriteLine(e);                Close();            }        }        private void Close()        {            if (clientSocket!=null)                        clientSocket.Close();            server.RemoveClient(this);        }        private void OnProcessMessage(RequesCode requesCode, ActionCode actionCode, string data)        {            server.HandleRequest(requesCode, actionCode, data, this);        }    }}
Server:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;using GameServer.Controller;using Common;namespace GameServer.Servers{    class Server    {        private IPEndPoint ipEndPoint;        private Socket serverSocket;        private List<Client> clientList;        private ControllerManager controllerManager;        public Server()        {        }        public Server(string IpStr, int port)        {            controllerManager = new ControllerManager(this);            SetIpAndPort(IpStr, port);        }        public void  SetIpAndPort(string IpStr, int port)        {            ipEndPoint = new IPEndPoint(IPAddress.Parse(IpStr), port);        }        public void Start()        {            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            serverSocket.Bind(ipEndPoint);            serverSocket.Listen(0);            serverSocket.BeginAccept(AcceptCallBack, null);        }                public void AcceptCallBack(IAsyncResult ar)        {            Socket clientSocket = serverSocket.EndAccept(ar); //            Client client = new Client(clientSocket,this);            client.Start();            clientList.Add(client); //添加到list集合        }        public void RemoveClient(Client client)        {            lock (clientList) //锁定移除            {                clientList.Remove(client);            }                   }        /// <summary>        /// 给Clinet客户端发起相应        /// </summary>        /// <param name="clinet"></param>        /// <param name="requestCode"></param>        /// <param name="data"></param>        public  void SentResponse(Client clinet,RequesCode requestCode,string data)        {            //TODO给客户端相应        }        /// <summary>        /// Manager跟server交互 server跟Clinet交互        /// </summary>        /// <param name="requesCode"></param>        /// <param name="actionCode"></param>        /// <param name="data"></param>        /// <param name="client"></param>        public  void HandleRequest(RequesCode requesCode, ActionCode actionCode, string data, Client client)        {            controllerManager.HandelRequset(requesCode, actionCode, data, client);        }    }}

14,数据的打包和数据的发送到客户端

Message:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Common;namespace GameServer.Servers{    class Message    {        private byte[] data = new byte[1024]; //这个数组用来存取我们读取到的数据,让消息的长度最少能存的下1024 ,如果说最大的消息数组存不下的话 就没办法完整的读取这条消息了        //如果消息不完整 我们不处理。我们会读取下一条        private int startIndex = 0;//标识位,表示现在数据存储在什么位置了 ,如果没数据 sartIndex从0开始,如果存了10个之后 这个startIndex就等于10;                                   //再来数据的话就从10开始存,同时也代表了存了多少个字节的数据。        /// <summary>        /// 这个数组用来存取我们读取到的数据        /// </summary>        public byte[] Data        {            get { return data; }        }        /// <summary>        /// 代表存储了多少个字节的数据  开始索引        /// </summary>        public int StartIndex        {            get { return startIndex; }        }        /// <summary>        /// 剩余的空间        /// </summary>        public int RemaniSize        {            get { return data.Length - startIndex; }        }        /// <summary>        /// 更新了多少数据        /// </summary>        /// <param name="count"></param>        //public void AddCount(int count)        //{        //    startIndex += count;        //}        /// <summary>        /// 解析数据或者叫做读取数据  newDataAmount数量        /// </summary>        public void ReadMessage(int newDataAmount,Action<RequesCode,ActionCode,string> processDataCallback)        {            startIndex += newDataAmount;            while (true)            {                if (startIndex <= 4) return;                int count = BitConverter.ToInt32(data, 0);//这个只占前四个字节  ,变成int 就是数据长度                if ((startIndex - 4) >= count) //(startIndex-4)是剩余数据的长度 ,大于count 就说明数据是完整的                {                    //string s = Encoding.UTF8.GetString(data, 4, count);                    //Console.WriteLine("解析出来一条数据 :" + s);                    RequesCode requestCode=(RequesCode)   BitConverter.ToInt32(data, 4); ///解析出来的是ResourceCode                    ActionCode actionCode = (ActionCode)BitConverter.ToInt32(data, 8); ///解析出来的是ActionCode                    string s = Encoding.UTF8.GetString(data, 12,count-8);                    processDataCallback(requestCode, actionCode, s);                    Array.Copy(data, count + 4, data, 0, startIndex - 4 - count);                    startIndex -= (count + 4); //移动完了之后更新startIndex                }                else                {                    break;                }            }                  }              /// <summary>        /// 用于组合包装        /// </summary>        /// <param name="requsetData"></param>        /// <param name="data"></param>        /// <returns></returns>        public static byte[] PakeDaa(RequesCode requsetData,string data)        {            byte[] requsetCodeBytes = BitConverter.GetBytes((int)requsetData);            byte[] dataBytes = Encoding.UTF8.GetBytes(data);            int dataAmount = requsetCodeBytes.Length + dataBytes.Length;            byte[] dataAmountBytes = BitConverter.GetBytes(dataAmount);       return      dataAmountBytes.Concat(requsetCodeBytes).Concat(dataBytes).ToArray();        }    }}

Clinet 类:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;using Common;namespace GameServer.Servers{    class Client    {        private Socket clientSocket;        private Server server;        private Message messgae = new Message();        public Client()        {        }        public Client(Socket clientSocket, Server server)        {            this.clientSocket = clientSocket;            this.server = server;        }        public void Start()        {            clientSocket.BeginReceive(messgae.Data, messgae.StartIndex, messgae.RemaniSize,SocketFlags.None,AsyncCallback,null);        }        public void AsyncCallback(IAsyncResult ar){            try            {                int count = clientSocket.EndReceive(ar);                if (count == 0)                {                    Close();                }                messgae.ReadMessage(count,OnProcessMessage);                Start();                //TODO 处理接收到的数据            }            catch (Exception e)            {                Console.WriteLine(e);                Close();            }        }        private void Close()        {            if (clientSocket!=null)                        clientSocket.Close();            server.RemoveClient(this);        }        private void OnProcessMessage(RequesCode requesCode, ActionCode actionCode, string data)        {            server.HandleRequest(requesCode, actionCode, data, this);        }              public void Send(RequesCode requsetCode,string data)        {            byte[] bytes = Message.PakeDaa(requsetCode, data);            clientSocket.Send(bytes);        }    }}

Server类:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;using GameServer.Controller;using Common;namespace GameServer.Servers{    class Server    {        private IPEndPoint ipEndPoint;        private Socket serverSocket;        private List<Client> clientList;        private ControllerManager controllerManager;        public Server()        {        }        public Server(string IpStr, int port)        {            controllerManager = new ControllerManager(this);            SetIpAndPort(IpStr, port);        }        public void  SetIpAndPort(string IpStr, int port)        {            ipEndPoint = new IPEndPoint(IPAddress.Parse(IpStr), port);        }        public void Start()        {            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            serverSocket.Bind(ipEndPoint);            serverSocket.Listen(0);            serverSocket.BeginAccept(AcceptCallBack, null);        }                public void AcceptCallBack(IAsyncResult ar)        {            Socket clientSocket = serverSocket.EndAccept(ar); //            Client client = new Client(clientSocket,this);            client.Start();            clientList.Add(client); //添加到list集合        }        public void RemoveClient(Client client)        {            lock (clientList) //锁定移除            {                clientList.Remove(client);            }                   }        /// <summary>        /// 给Clinet客户端发起相应        /// </summary>        /// <param name="clinet"></param>        /// <param name="requestCode"></param>        /// <param name="data"></param>        public  void SentResponse(Client clinet,RequesCode requestCode,string data)        {            //TODO给客户端相应            clinet.Send(requestCode, data);        }        /// <summary>        /// Manager跟server交互 server跟Clinet交互        /// </summary>        /// <param name="requesCode"></param>        /// <param name="actionCode"></param>        /// <param name="data"></param>        /// <param name="client"></param>        public  void HandleRequest(RequesCode requesCode, ActionCode actionCode, string data, Client client)        {            controllerManager.HandelRequset(requesCode, actionCode, data, client);        }    }}

15,创建ConnHelper,数据库连接的创建和关闭。



然后门在Tool文件夹下新建ConnHelper类:

并且在mysql数据库中创建一个game01

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using MySql.Data.MySqlClient;namespace GameServer.Tool{    /// <summary>    /// 用来建立跟服务端的连接    /// </summary>    class ConnHelper    {        public const string CONNECTIONDTRING = "dataSource=127.0.0.1;port=3306;database=game01;user=root;password=root";        public static MySqlConnection Connect()        {            MySqlConnection conn = new MySqlConnection(CONNECTIONDTRING);            try            {                conn.Open();                return conn;            }            catch (Exception e)            {                Console.WriteLine("连接数据库出现异常" +e );                return null;            }        }        public static  void CloseConnection(MySqlConnection conn)        {                           if (conn!=null)                {                    conn.Close();                }                else                {                    Console.WriteLine(" MysqlConnection不能为空");                }                    }    }}

然后再对客户端Client进行修改:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;using Common;using MySql.Data.MySqlClient;using GameServer.Tool;namespace GameServer.Servers{    class Client    {        private Socket clientSocket;        private Server server;        private Message messgae = new Message();        private MySqlConnection mysqlConn;        public Client()        {        }        public Client(Socket clientSocket, Server server)        {            this.clientSocket = clientSocket;            this.server = server;            mysqlConn = ConnHelper.Connect();        }        public void Start()        {            clientSocket.BeginReceive(messgae.Data, messgae.StartIndex, messgae.RemaniSize,SocketFlags.None,AsyncCallback,null);        }        public void AsyncCallback(IAsyncResult ar){            try            {                int count = clientSocket.EndReceive(ar);                if (count == 0)                {                    Close();                }                messgae.ReadMessage(count,OnProcessMessage);                Start();                //TODO 处理接收到的数据            }            catch (Exception e)            {                Console.WriteLine(e);                Close();            }        }        private void Close()        {            ConnHelper.CloseConnection(mysqlConn);            if (clientSocket!=null)                        clientSocket.Close();            server.RemoveClient(this);        }        /// <summary>        /// 消息的解析        /// </summary>        /// <param name="requesCode"></param>        /// <param name="actionCode"></param>        /// <param name="data"></param>        private void OnProcessMessage(RequesCode requesCode, ActionCode actionCode, string data)        {            server.HandleRequest(requesCode, actionCode, data, this);        }      /// <summary>      /// 消息的发送      /// </summary>      /// <param name="requsetCode"></param>      /// <param name="data"></param>        public void Send(RequesCode requsetCode,string data)        {            byte[] bytes = Message.PakeDaa(requsetCode, data);            clientSocket.Send(bytes);        }    }}

12,导入开发好的UI框架和目录框架:

资源 :链接: https://pan.baidu.com/s/1hrSJUmS密码: xe7b

新建Unity3D工程 ; SkUIFramework

13,导入游戏素材

导入资源 Res 

14 ,游戏客户端和架构分析

架构图如下:

Access文件夹下新建Scripts文件夹 下面创建GameFacade 然后新建一个Manager文件夹 ,创建BaseManager, 然后修改UIManager,新建 CameraManager AudioManager,PlayerManager,RequestManager ClientManager,都继承自BaseManager 。

BaseManager类如下、;

using System.Collections;using System.Collections.Generic;using UnityEngine;public class BaseManager  {    /// <summary>    /// 生命的开始    /// </summary>    public virtual void OnInit() { }    /// <summary>    /// 生命的结束    /// </summary>    public virtual void OnDestory() { }}

然后在GameFacade 类:

using System.Collections;using System.Collections.Generic;using UnityEngine;public class GameFacade : MonoBehaviour {    private UIManager uiMng;    private AudioManager audioMng;    private PlayerManager playerMng;    private CameraManager cameraMng;    private RequestManager requesMng;    private ClientManager clientMng;void Start () {        InitManager();    }void Update () {}    private void InitManager()    {        uiMng = new UIManager();        audioMng = new AudioManager();        playerMng = new PlayerManager();        cameraMng = new CameraManager();        requesMng = new RequestManager();        clientMng = new ClientManager();        uiMng.OnInit();        audioMng.OnInit();        playerMng.OnInit();        cameraMng.OnInit();        requesMng.OnInit();        clientMng.OnInit();    }    private void DestoryManager()    {        uiMng.OnDestory();        audioMng.OnDestory();        playerMng.OnDestory();        cameraMng.OnDestory();        requesMng.OnDestory();        clientMng.OnDestory();    }    private void OnDestory()    {        DestoryManager();    }}


13,开发ClinetManager,跟服务器端的连接和关闭

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Net.Sockets;using System.Net;using UnityEngine;/// <summary>/// 这个是用来管理跟服务器端的Socket连接/// </summary>public  class ClientManager:BaseManager{    public const string IP = "127.0.0.1";    public const int PORT = 6688;    private Socket clinetSocket;    public override void OnInit()    {        base.OnInit();        clinetSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);        try        {            clinetSocket.Connect(IP, PORT);                    }        catch (Exception e)        {            Debug.LogWarning("无法连接到服务器端请检查你的网络"+e);        }    }    public override void OnDestory()    {        base.OnDestory();                try        {            clinetSocket.Close();        }        catch (Exception e)        {            Debug.LogWarning("无法关闭跟服务端的连接" + e);        }    }}


请看下集

原创粉丝点击