Socket多人联网游戏学习记录

连接Mysql 准备

  1. 创建数据库Test001
  2. 创建一个表,TableName为user
  3. 创建新列,id(设置为主键,勾选PK NN AI),username,password
  4. 查询刚才新建的表格Select Row
  5. 添加新数据

使用mysql插件,找到mysql安装目录,C:\Program Files (x86)\MySQL\Connector.NET 6.9\Assemblies\v4.5\MySql.Data.dll(选择相近版本)

查询数据库

首先导入,数据库连接插件,引入Mysql.Data.MysqlClient

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MySql.Data.MySqlClient;

namespace MySqlTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string connStr = "Database=test001;Data Source=127.0.0.1;port=3306;user=root;pwd=root;";  //连接数据库语句(忽略大小写)
            MySqlConnection conn = new MySqlConnection(connStr);  //创建连接对象

            conn.Open();  //打开通道

            //MySqlCommand cmd = new MySqlCommand("select * from user where id = 1", conn);  //读取id为 1的记录
            MySqlCommand cmd = new MySqlCommand("select * from user", conn);

            MySqlDataReader reader= cmd.ExecuteReader();  //执行查询记录

            //if(reader.HasRows)  //判断数据是否为空
            //{
            //    reader.Read();  //只读取一行表数据
            //    //获取数据
            //    string username = reader.GetString("username");
            //    string password = reader.GetString("password");
            //    Console.WriteLine("username:" + username);
            //    Console.WriteLine("password:" + password);
            //}

            while (reader.Read())  //行数据不为空,一直执行
            {
                //获取数据
                string username = reader.GetString("username");
                string password = reader.GetString("password");
                Console.WriteLine("username:" + username);
                Console.WriteLine("password:" + password);
            }

            //关闭数据流
            reader.Close();
            conn.Close();
            Console.ReadKey();
        }
    }
}

插入数据

插入数据时,要防止sql注入问题。

            //注意 Sql注入问题
            string user = "newUser"; string pwd = "newPwd";
            MySqlCommand cmd = new MySqlCommand("insert into user set username=@user,password=@pwd", conn);  //@符号后面是用户自定义的插入内容
            cmd.Parameters.AddWithValue("user", user);  //数值绑定
            cmd.Parameters.AddWithValue("pwd", pwd);
            cmd.ExecuteNonQuery();  //执行非查询命令

删除数据

            MySqlCommand cmd = new MySqlCommand("delete from  user where id =@id", conn);
            cmd.Parameters.AddWithValue("id", 3);  //删除id为3的记录
            cmd.ExecuteNonQuery();

更新数据

            //更新id为 2 的密码为“updatePsd”
            string pwd = "updatePsd";
            MySqlCommand cmd = new MySqlCommand("update user set password=@pwd where id = 2", conn);
            cmd.Parameters.AddWithValue("pwd", pwd);
            cmd.ExecuteNonQuery();

游戏服务器端口分层架构

  1. Controller
  2. DAO
  3. Model
  4. Server
  5. Tool

Client不和Controller直接交互,需要通过Server间接处理数据

数据封装打包处理

定义一个Action方法,讲解析出的RequestCode,Data和数据长度,注入这个方法中。外部可以通过回调函数直接拿到处理过的数据。

        /// <summary>
        /// 解析数据或者叫做读取数据
        /// </summary>
        public void ReadMessage(int newDataAmount,Action<RequestCode,ActionCode,string> processDataCallBack)  //参数用于传递解析出来的数据
        {
            startIndex += newDataAmount;
            while (true)
            {
                if (startIndex <= 4) return;
                int count = BitConverter.ToInt32(data, 0);
                if ((startIndex - 4) >= count)
                {
                    //Console.WriteLine(startIndex);
                    //Console.WriteLine(count);
                    //string s = Encoding.UTF8.GetString(data, 4, count);
                    //Console.WriteLine("解析出来一条数据:" + s);

                    //RequestCode占前4位,ActionCode占4位,data从第12位开始
                    RequestCode requestCode=(RequestCode) BitConverter.ToInt32(data, 4);
                    ActionCode actionCode = (ActionCode)BitConverter.ToInt32(data, 8);
                    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);
                }
                else
                {
                    break;
                }
            }
        }

外部:

                //TODO 处理接收到的数据
                msg.ReadMessage(count,OnProcessMessage);  //处理前四个字节的校验位
        //ReadMessage 的回调函数,每次读取消息都会调用此方法
        private void OnProcessMessage(RequestCode requestCode,ActionCode actionCode,string data)
        {
            server.HandleRequest(requestCode,actionCode,data,this);  //不在客户端处理数据
        }

创建客户端和服务器共享文件Common

namespace Common
{
    public enum ActionCode
    {
        None,
    }
}
/// <summary>
/// Commom 类库,服务端和客户端共同使用
/// </summary>
namespace Common
{
    //每一个RequestCode对应一个Controller处理相关事件
    public enum RequestCode
    {
        None,
    }
}

ControllerManager 统一管理客户端对应Controller

Client读取服务器发来的数据,把相关回复方法交给对应的Controller处理。

相关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;
/// <summary>
/// 此类之和server交互
/// </summary>
namespace GameServer.Controller
{
    class ControllerManager
    {
        private Dictionary<RequestCode, BaseController> controllerManager = new Dictionary<RequestCode, BaseController>();  //存储所有的controller
        private Server server;   //因为server经常需要被访问,所以应该制作成单独变量

        public ControllerManager(Server server)
        {
            this.server = server;
            InitController();
        }

        private void InitController()
        {
            DefaultController defaultController = new DefaultController();
            controllerManager.Add(defaultController.RequestCode, defaultController);  //默认的控制器
        }

        //通过RequestCode来找到对应的ActionCode,然后执行方法调用
        public void HandleRequest(RequestCode requestCode, ActionCode actionCode, string data, Client client)
        {
            BaseController controller;
            bool isGet = controllerManager.TryGetValue(requestCode, out controller);  //尝试查找requestCode
            if (isGet == false)
            {
                Console.WriteLine("无法得到[" + requestCode + "]所对应的Controller");
                return;
            }
            string methodName = Enum.GetName(typeof(ActionCode), actionCode);  //找到对应的方法
            MethodInfo mi = controller.GetType().GetMethod(methodName); //在Controller下面执行此ActionCode对应的方法
            if (mi == null)
            {
                Console.WriteLine("在Controller[" + controller.GetType() + "]中没有对应的处理方法:" + methodName);
                return;
            }
            object[] parameters = new object[] { data, client, server };
            object o = mi.Invoke(controller, parameters);
            if (o == null||string.IsNullOrEmpty(o as string))
            {
                return;
            }
            server.SendResponse(client, requestCode, o as string);//向服务器发送请求的方法          
        }
    }
}

客户端架构

BaseManager:包括UIManager,CameraManager,AudioManager,PlayerManager,RequestManager(管理所有请求)ClientManager(这个用来管理跟服务器端连接的socket)。
用来统一管理以上的控制器。
GameFacade:包括BaseManager。GameFacade 充当外观者或中介者,各个模块的交互通过Facade来进行,可以减少耦合性。
这里写图片描述

BaseManager管理所有控制器

创建一个带有GameFacade参数的构造方法,在子类中可以使用GameFacade来与其他Manager模块交互。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 用来管理所有的Manager
/// </summary>
public class BaseManager
{
    protected GameFacade facade;  //受保护类型,只能被子类调用
    public BaseManager(GameFacade facade)
    {
        this.facade = facade;
    }
    public virtual void OnInit() { }
    public virtual void OnDestroy() { }
}

子类在重写的时候,也应该重写父类的构造方法。

    public UIManager(GameFacade facade) : base(facade)
    {
        ParseUIPanelTypeJson();
    }

GameFacade中介者

创建GameFacade来访问其他模块的代码,从而减少代码之前的耦合性,方便后期代码的的升级和修改。继承至MonoBehaviour,当代码挂载到游戏物体身上使时,完成OnInit的初始化。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Common;
/// <summary>
/// 用于不同Manager之间的交互的
/// </summary>
public class GameFacade:MonoBehaviour {

    private static GameFacade _instance;
    public static GameFacade Instance  //单例
    {
        get { return _instance; }
    }

    private UIManager uiMng;
    private AudioManager audioMng;
    private PlayerManager playerMng;
    private CameraManager cameraMng;
    private RequestManager requesrMng;
    private ClientManager clientMng;

    private void Awake()
    {
        if(_instance!=null)
        {
            Destroy(this.gameObject);
            return;
        }
        else
        {
            _instance = this;
        }
    }

    // Use this for initialization
    void Start () {
        InitManager();
    }


    //生命周期
    private void InitManager()
    {
        //初始化
        uiMng = new UIManager(this);
        audioMng = new AudioManager(this);
        playerMng = new PlayerManager(this);
        cameraMng = new CameraManager(this);
        requesrMng = new RequestManager(this);
        clientMng = new ClientManager(this);

        uiMng.OnInit();
        audioMng.OnInit();
        playerMng.OnInit();
        cameraMng.OnInit();
        requesrMng.OnInit();
        clientMng.OnInit();
    }
    private void DestroyManager()
    {
        uiMng.OnDestroy();
        audioMng.OnDestroy();
        playerMng.OnDestroy();
        cameraMng.OnDestroy();
        requesrMng.OnDestroy();
        clientMng.OnDestroy();
    }

    private void OnDestroy()
    {
        DestroyManager();
    }

    public void AddRequest(RequestCode requestCode, BaseRequest baseRequest)
    {
        requesrMng.AddRequest(requestCode, baseRequest);
    }

    public void RemoveRequest(RequestCode requestCode)
    {
        requesrMng.RemoveRequest(requestCode);
    }

    public void HandleReponse(RequestCode requestCode, string data)
    {
        requesrMng.HandleReponse(requestCode,data);
    }
}

客户端的Message方法修改

因为客户端在打包数据的时候需要把RequestCode,ActionCode和数据打包在一起发送到服务器端。所有修改以下代码片段:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common;

public class Message 
{
    private byte[] data = new byte[1024];
    private int startIndex = 0;//我们存取了多少个字节的数据在数组里面

    //public void AddCount(int count)
    //{
    //    startIndex += count;
    //}
    public byte[] Data
    {
        get { return data; }
    }
    public int StartIndex
    {
        get { return startIndex; }
    }
    public int RemainSize
    {
        get { return data.Length - startIndex; }
    }
    /// <summary>
    /// 解析数据或者叫做读取数据
    /// </summary>
    public void ReadMessage(int newDataAmount, Action<RequestCode, string> processDataCallBack)  //参数用于传递解析出来的数据
    {
        startIndex += newDataAmount;
        while (true)
        {
            if (startIndex <= 4) return;
            int count = BitConverter.ToInt32(data, 0);
            if ((startIndex - 4) >= count)
            {
                //Console.WriteLine(startIndex);
                //Console.WriteLine(count);
                //string s = Encoding.UTF8.GetString(data, 4, count);
                //Console.WriteLine("解析出来一条数据:" + s);

                //RequestCode占前4位,ActionCode占4位,data从第12位开始
                RequestCode requestCode = (RequestCode)BitConverter.ToInt32(data, 4);
                //ActionCode actionCode = (ActionCode)BitConverter.ToInt32(data, 8);
                string s = Encoding.UTF8.GetString(data, 8, count - 4);
                processDataCallBack(requestCode, s);
                Array.Copy(data, count + 4, data, 0, startIndex - 4 - count);
                startIndex -= (count + 4);
            }
            else
            {
                break;
            }
        }
    }

    /// <summary>
    /// 封装 数据长度,RequestCode,data 为字节流
    /// </summary>
    /// <param name="requestCode"></param>
    /// <param name="data"></param>
    public static byte[] PackData(RequestCode requestCode, string data)  //类似工具方法,可以使用static方便调用
    {
        byte[] requestCodeBytes = BitConverter.GetBytes((int)requestCode);
        byte[] dataBytes = Encoding.UTF8.GetBytes(data);
        int dataAmount = requestCodeBytes.Length + dataBytes.Length;
        byte[] dataAmountBytes = BitConverter.GetBytes(dataAmount);
        return (byte[])dataAmountBytes.Concat(requestCodeBytes).Concat(dataBytes);
    }

    //客户端发送的方法
    public static byte[] PackData(RequestCode requestCode,ActionCode actionCode, string data)  //类似工具方法,可以使用static方便调用
    {
        byte[] requestCodeBytes = BitConverter.GetBytes((int)requestCode);
        byte[] actionCodeBytes = BitConverter.GetBytes((int)actionCode);
        byte[] dataBytes = Encoding.UTF8.GetBytes(data);
        int dataAmount = requestCodeBytes.Length + dataBytes.Length+actionCodeBytes.Length;
        byte[] dataAmountBytes = BitConverter.GetBytes(dataAmount);
        return (byte[])dataAmountBytes.Concat(requestCodeBytes).Concat(actionCodeBytes).Concat(dataBytes);  //数据长度,RequestCode,ActionCode,数据
    }
}


UI框架的使用

在使用UI框架的时候,一定要注意命名的规范性,这将也是我们能否快速开发的关键因素。

  1. 每一个Panel面板都需要创建一个同名脚本拿来管理这个面板,此脚步需要继承自BasePanel
  2. 将创建好的面板做成Prefab,放到Resources文件夹下
  3. 修改UIPanelType.cs脚本
  4. 修改UIPanelType.json脚本(存储面板所在路径)
  5. 取消UIManager的单例模式,继承自BaseManager管理
  6. UIManager通过构造方法得到GameGacade的引用,通过重写父类的OnInit方法来初始化

BaseManager:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 用来管理所有的Manager
/// </summary>
public class BaseManager
{
    protected GameFacade facade;  //受保护类型,只能被子类调用
    public BaseManager(GameFacade facade)
    {
        this.facade = facade;
    }
    public virtual void OnInit() { }
    public virtual void OnDestroy() { }
}

UiManager:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;

public class UIManager:BaseManager {

    public UIManager(GameFacade facade) : base(facade)
    {
        ParseUIPanelTypeJson();
    }

    public override void OnInit()  //在GameFacade调用
    {
        base.OnInit();
        PushPanel(UIPanelType.Message);
        PushPanel(UIPanelType.Start);
    }

    /// 
    /// 单例模式的核心
    /// 1,定义一个静态的对象 在外界访问 在内部构造
    /// 2,构造方法私有化

    //private static UIManager _instance;

    //public static UIManager Instance
    //{
    //    get
    //    {
    //        if (_instance == null)
    //        {
    //            _instance = new UIManager();
    //        }
    //        return _instance;
    //    }
    //}

    private Transform canvasTransform;
    private Transform CanvasTransform
    {
        get
        {
            if (canvasTransform == null)
            {
                canvasTransform = GameObject.Find("Canvas").transform;
            }
            return canvasTransform;
        }
    }
    private Dictionary<UIPanelType, string> panelPathDict;//存储所有面板Prefab的路径
    private Dictionary<UIPanelType, BasePanel> panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件
    private Stack<BasePanel> panelStack;
    private MessagePanel msgPanel;

    /// <summary>
    /// 把某个页面入栈,把某个页面显示在界面上
    /// </summary>
    public void PushPanel(UIPanelType panelType)
    {
        if (panelStack == null)
            panelStack = new Stack<BasePanel>();

        //判断一下栈里面是否有页面
        if (panelStack.Count > 0)
        {
            BasePanel topPanel = panelStack.Peek();
            topPanel.OnPause();  //暂停栈顶的UI
        }

        //实例化UI界面
        BasePanel panel = GetPanel(panelType);
        panel.OnEnter();
        panelStack.Push(panel);  //加入新的UI
    }
    /// <summary>
    /// 出栈 ,把页面从界面上移除
    /// </summary>
    public void PopPanel()
    {
        if (panelStack == null)
            panelStack = new Stack<BasePanel>();

        if (panelStack.Count <= 0) return;

        //关闭栈顶页面的显示
        BasePanel topPanel = panelStack.Pop();
        topPanel.OnExit();

        if (panelStack.Count <= 0) return;
        BasePanel topPanel2 = panelStack.Peek();
        topPanel2.OnResume();

    }

    /// <summary>
    /// 根据面板类型 得到实例化的面板
    /// </summary>
    /// <returns></returns>
    private BasePanel GetPanel(UIPanelType panelType)
    {
        if (panelDict == null)
        {
            panelDict = new Dictionary<UIPanelType, BasePanel>();
        }

        //BasePanel panel;
        //panelDict.TryGetValue(panelType, out panel);//TODO

        BasePanel panel = panelDict.TryGet(panelType);

        if (panel == null)
        {
            //如果找不到,那么就找这个面板的prefab的路径,然后去根据prefab去实例化面板
            //string path;
            //panelPathDict.TryGetValue(panelType, out path);
            string path = panelPathDict.TryGet(panelType);
            GameObject instPanel = GameObject.Instantiate(Resources.Load(path)) as GameObject;
            instPanel.transform.SetParent(CanvasTransform,false);
            instPanel.GetComponent<BasePanel>().UIMng= this;  //实例化UIManager
            panelDict.Add(panelType, instPanel.GetComponent<BasePanel>());
            return instPanel.GetComponent<BasePanel>();
        }
        else
        {
            return panel;
        }

    }

    [Serializable]
    class UIPanelTypeJson
    {
        public List<UIPanelInfo> infoList;
    }
    private void ParseUIPanelTypeJson()
    {
        panelPathDict = new Dictionary<UIPanelType, string>();

        TextAsset ta = Resources.Load<TextAsset>("UIPanelType");

        UIPanelTypeJson jsonObject = JsonUtility.FromJson<UIPanelTypeJson>(ta.text);

        foreach (UIPanelInfo info in jsonObject.infoList) 
        {
            //Debug.Log(info.panelType);
            panelPathDict.Add(info.panelType, info.path);
        }
    }

    //给MsgPaneld赋值
    public void InjectMsgPanel(MessagePanel msgPanel)
    {
        this.msgPanel = msgPanel;
    }


    public void ShowMessage(string msg)
    {
        if(msgPanel==null)
        {
            Debug.Log("无法显示提示信息,MsgPanel为空");
        }
        else
        {
            msgPanel.ShowMessage(msg);
        }
    }

    public void ShowMessageSync(string msg)
    {
        if (msgPanel == null)
        {
            Debug.Log("无法显示提示信息,MsgPanel为空");
        }
        else
        {
            msgPanel.ShowMessageSync(msg);
        }
    }

    /// <summary>
    /// just for test
    /// </summary>
    //public void Test()
    //{
    //    string path ;
    //    panelPathDict.TryGetValue(UIPanelType.Knapsack,out path);
    //    Debug.Log(path);
    //}
}

GameFacede:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Common;
/// <summary>
/// 用于不同Manager之间的交互的
/// </summary>
public class GameFacade:MonoBehaviour {

    private static GameFacade _instance;
    public static GameFacade Instance  //单例
    {
        get { return _instance; }
    }

    private UIManager uiMng;
    private AudioManager audioMng;
    private PlayerManager playerMng;
    private CameraManager cameraMng;
    private RequestManager requesrMng;
    private ClientManager clientMng;

    private void Awake()
    {
        if(_instance!=null)
        {
            Destroy(this.gameObject);
            return;
        }
        else
        {
            _instance = this;
        }
    }

    // Use this for initialization
    void Start () {
        InitManager();
    }


    //生命周期
    private void InitManager()
    {
        //初始化
        uiMng = new UIManager(this);
        audioMng = new AudioManager(this);
        playerMng = new PlayerManager(this);
        cameraMng = new CameraManager(this);
        requesrMng = new RequestManager(this);
        clientMng = new ClientManager(this);

        uiMng.OnInit();
        audioMng.OnInit();
        playerMng.OnInit();
        cameraMng.OnInit();
        requesrMng.OnInit();
        clientMng.OnInit();
    }
    private void DestroyManager()
    {
        uiMng.OnDestroy();
        audioMng.OnDestroy();
        playerMng.OnDestroy();
        cameraMng.OnDestroy();
        requesrMng.OnDestroy();
        clientMng.OnDestroy();
    }

    private void OnDestroy()
    {
        DestroyManager();
    }

    public void AddRequest(ActionCode actionCode, BaseRequest baseRequest)
    {
        requesrMng.AddRequest(actionCode, baseRequest);
    }

    public void RemoveRequest(ActionCode actionCode)
    {
        requesrMng.RemoveRequest(actionCode);
    }

    public void HandleReponse(ActionCode actionCode, string data)
    {
        requesrMng.HandleReponse(actionCode, data);
    }

    public void ShowMessage(string msg)
    {
        uiMng.ShowMessage(msg);
    }

    public void SendRequest(RequestCode requestCode, ActionCode actionCode, string data)
    {
        clientMng.SendRequest(requestCode, actionCode, data);
    }
}

UI面板的切换

UIManager首先加载了UIPanelType.Start的面板,对应面板的按钮交互放在对应Panel 脚本下面。

StartPanel:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;

public class StartPanel : BasePanel {

    private Button loginButton;
    private Animator loginAnim;

    public override void OnEnter()
    {
        base.OnEnter();
         loginButton = transform.Find("LoginButton").GetComponent<Button>();
        loginAnim = loginButton.GetComponent<Animator>();
        loginButton.onClick.AddListener(OnLoginCLick);  //注册点击事件
        //loginButton.transform.DOScale(1, 0.5f);
    }

    private void OnLoginCLick()
    {
        uiMng.PushPanel(UIPanelType.Login); //加载登录面板
    }


}

这里写图片描述
通过RequestCode区分Controller
通过ActionCode区分Request

LoginPanel:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
using Common;

public class LoginPanel : BasePanel {

    private Button closeButton;  //关闭按钮
    private Button loginButton;  //登录按钮
    private InputField userInput;
    private InputField pwdInput;
    private LoginRequest loginRequest;  //登录脚本

    void Start()
    {
        userInput = transform.Find("UsernameLabel/UsernameInput").GetComponent<InputField>();
        pwdInput = transform.Find("PasswordLabel/PasswordInput").GetComponent<InputField>();

        closeButton = transform.Find("CloseButton").GetComponent<Button>();
        closeButton.onClick.AddListener(OnClsoeButton);
        loginButton = transform.Find("LoginButton").GetComponent<Button>();
        loginButton.onClick.AddListener(OnLoginClick);

        loginRequest = GetComponent<LoginRequest>();
    }

    //登录校验
    private void OnLoginClick()
    {
        string msg = "";
        if(string.IsNullOrEmpty(userInput.text))
        {
            msg += "用户名不能为空 ";
        }
        if(string.IsNullOrEmpty(pwdInput.text))
        {
            msg += "密码不能为空";
        }

        if(msg!="")
        {
            uiMng.ShowMessage(msg);
            return;
        }

        //TODO请求服务器端
        loginRequest.SendRequest(userInput.text, pwdInput.text);
    }

    public void OnLoginResponse(ReturnCode returnCode)
    {
        if(returnCode==ReturnCode.Success)
        {
            //TODO登录成功
            Debug.Log("登录成功!");
        }
        else
        {
            uiMng.ShowMessageSync("用户名或密码错误,请重新输入!");
        }
    }

    private void OnClsoeButton()
    {
        //print("关闭");
        uiMng.PopPanel();
    }

    public override void OnEnter()
    {
        base.OnEnter();
        ShowPanel();      
    }

    private void ShowPanel()
    {
        gameObject.SetActive(true);
        RectTransform rectPos = GetComponent<RectTransform>();
        Vector3 temp = rectPos.position;
        temp.x = -1000;
        rectPos.position = temp;  //把物体移到外边
        rectPos.DOAnchorPosX(0, 0.5f);  //移到目的地
        transform.DOScale(1, 0.5f);
    }

    private void HidePanel()
    {
        transform.DOScale(0, 0.5f);
        transform.DOLocalMoveX(-1000, 0.5f).OnComplete(() => gameObject.SetActive(false));
    }

    public override void OnExit()
    {
        base.OnExit();
        HidePanel();
    }

    public override void OnResume()
    {
        base.OnResume();
        ShowPanel();
    }

    //public override void OnPause()
    //{
    //    base.OnPause();
    //    HidePanel();
    //}


}

服务器响应请求:

Server:

        public Server() { }
        public Server(string ipStr,int port)
        {
            controllerManager = new ControllerManager(this);
            SetIpEndPort(ipStr,port);
        }
        public void SetIpEndPort(string ipStr,int port)
        {
            ipEndPort = new IPEndPoint(IPAddress.Parse(ipStr), port);
        }

        public void Start()  //启动监听
        {
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            serverSocket.Bind(ipEndPort);  //绑定ip和端口号
            serverSocket.Listen(0);  //一直监听
            serverSocket.BeginAccept(AcceptCallBack, null);  //回掉
        }

        private void AcceptCallBack(IAsyncResult ar)
        {
            Socket clientSocket = serverSocket.EndAccept(ar);  //获得客户端的连接
            Client client = new Client(clientSocket,this);
            client.Start();
            clientList.Add(client);
        }

Client:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using Common;
using MySql.Data.MySqlClient;
using GameServer.Tool;

namespace GameServer.Servers
{
    class Client
    {
        private Socket clientSocket;  //存储获取连接的Socket
        private Server server;  //server的相关处理
        private Message msg = new Message();  //创建一个字符处理类(处理前四个字节的校验位)
        private MySqlConnection mysqlConn;  //使对应Controller来获取mysql连接,从而进行一些操作

        public MySqlConnection MySqlConn
        {
            get { return mysqlConn; }
        }

        public Client() { }

        public Client(Socket clientSocket,Server server)  //在client初始化server
        {
            this.clientSocket = clientSocket;
            this.server = server;
            mysqlConn = ConnHelper.Connect();  //初始化数据库连接
        }

        public void Start()
        {
            //客户端接收缓存
            clientSocket.BeginReceive(msg.Data, msg.StartIndex, msg.RemainSize, SocketFlags.None, ReceiveCallBack, null);  //客户端开启监听
        }

        private void ReceiveCallBack(IAsyncResult ar)
        {
            try
            {
                int count = clientSocket.EndReceive(ar);
                if (count == 0)   //偏移量为0
                {
                    Close();
                }
                //TODO 处理接收到的数据
                msg.ReadMessage(count,OnProcessMessage);  //处理前四个字节的校验位
                Start();
            }
            catch(Exception e)
            {
                Console.WriteLine(e);
                Close();
            }
        }

        //ReadMessage 的回调函数,每次读取消息都会调用此方法
        private void OnProcessMessage(RequestCode requestCode,ActionCode actionCode,string data)
        {
            server.HandleRequest(requestCode,actionCode,data,this);  //不在客户端处理数据
        }

        private void Close()  //断开连接
        {
            ConnHelper.CloseConnectiom(mysqlConn);  //关闭数据库连接
            if (clientSocket != null)
                clientSocket.Close();
            server.RemoveClient(this);  //从服务器连接的客户端的集合移除
        }

        public void Send(ActionCode actionCode,string data)
        {
            byte[] bytes = Message.PackData(actionCode, data);
            clientSocket.Send(bytes);
        }
    }
}

ControllerManager数据处理:

        //通过RequestCode来找到对应的ActionCode,然后执行方法调用
        public void HandleRequest(RequestCode requestCode, ActionCode actionCode, string data, Client client)
        {
            BaseController controller;
            bool isGet = controllerManager.TryGetValue(requestCode, out controller);  //尝试查找requestCode
            if (isGet == false)
            {
                Console.WriteLine("无法得到[" + requestCode + "]所对应的Controller");
                return;
            }
            string methodName = Enum.GetName(typeof(ActionCode), actionCode);  //找到对应的方法
            MethodInfo mi = controller.GetType().GetMethod(methodName); //在Controller下面执行此ActionCode对应的方法
            if (mi == null)
            {
                Console.WriteLine("在Controller[" + controller.GetType() + "]中没有对应的处理方法:" + methodName);
                return;
            }
            object[] parameters = new object[] { data, client, server };
            object o = mi.Invoke(controller, parameters);
            if (o == null||string.IsNullOrEmpty(o as string))
            {
                return;
            }
            server.SendResponse(client, actionCode, o as string);//向服务器发送请求的方法          
        }

这就是封装啊,登录请求继承请求基础类,请求调用发送方法,发送方法属于游戏底层的类,底层又归游戏管理类管理

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值