您的位置:首页 > 其它

Socket多人联网游戏学习记录

2018-02-09 12:55 232 查看

连接Mysql 准备

创建数据库Test001

创建一个表,TableName为user

创建新列,id(设置为主键,勾选PK NN AI),username,password

查询刚才新建的表格Select Row

添加新数据

使用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();


游戏服务器端口分层架构

Controller

DAO

Model

Server

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 = (ActionCod
4000
e)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框架的时候,一定要注意命名的规范性,这将也是我们能否快速开发的关键因素。

每一个Panel面板都需要创建一个同名脚本拿来管理这个面板,此脚步需要继承自BasePanel

将创建好的面板做成Prefab,放到Resources文件夹下

修改UIPanelType.cs脚本

修改UIPanelType.json脚本(存储面板所在路径)

取消UIManager的单例模式,继承自BaseManager管理

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);//向服务器发送请求的方法
}


这就是封装啊,登录请求继承请求基础类,请求调用发送方法,发送方法属于游戏底层的类,底层又归游戏管理类管理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: