使用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();
Client不和Controller直接交互,需要通过Server间接处理数据
定义一个Action方法,讲解析出的RequestCode,Data和数据长度,注入这个方法中。外部可以通过回调函数直接拿到处理过的数据。
///
/// 解析数据或者叫做读取数据
///
public void ReadMessage(int newDataAmount,Actionstring> 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); //不在客户端处理数据
}
namespace Common
{
public enum ActionCode
{
None,
}
}
///
/// Commom 类库,服务端和客户端共同使用
///
namespace Common
{
//每一个RequestCode对应一个Controller处理相关事件
public enum RequestCode
{
None,
}
}
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;
///
/// 此类之和server交互
///
namespace GameServer.Controller
{
class ControllerManager
{
private Dictionary controllerManager = new Dictionary(); //存储所有的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来进行,可以减少耦合性。
创建一个带有GameFacade参数的构造方法,在子类中可以使用GameFacade来与其他Manager模块交互。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 用来管理所有的Manager
///
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来访问其他模块的代码,从而减少代码之前的耦合性,方便后期代码的的升级和修改。继承至MonoBehaviour,当代码挂载到游戏物体身上使时,完成OnInit的初始化。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Common;
///
/// 用于不同Manager之间的交互的
///
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);
}
}
因为客户端在打包数据的时候需要把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; }
}
///
/// 解析数据或者叫做读取数据
///
public void ReadMessage(int newDataAmount, Actionstring> 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;
}
}
}
///
/// 封装 数据长度,RequestCode,data 为字节流
///
///
///
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框架的时候,一定要注意命名的规范性,这将也是我们能否快速开发的关键因素。
BaseManager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 用来管理所有的Manager
///
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 Dictionarystring> panelPathDict;//存储所有面板Prefab的路径
private Dictionary panelDict;//保存所有实例化面板的游戏物体身上的BasePanel组件
private Stack panelStack;
private MessagePanel msgPanel;
///
/// 把某个页面入栈,把某个页面显示在界面上
///
public void PushPanel(UIPanelType panelType)
{
if (panelStack == null)
panelStack = new Stack();
//判断一下栈里面是否有页面
if (panelStack.Count > 0)
{
BasePanel topPanel = panelStack.Peek();
topPanel.OnPause(); //暂停栈顶的UI
}
//实例化UI界面
BasePanel panel = GetPanel(panelType);
panel.OnEnter();
panelStack.Push(panel); //加入新的UI
}
///
/// 出栈 ,把页面从界面上移除
///
public void PopPanel()
{
if (panelStack == null)
panelStack = new Stack();
if (panelStack.Count <= 0) return;
//关闭栈顶页面的显示
BasePanel topPanel = panelStack.Pop();
topPanel.OnExit();
if (panelStack.Count <= 0) return;
BasePanel topPanel2 = panelStack.Peek();
topPanel2.OnResume();
}
///
/// 根据面板类型 得到实例化的面板
///
///
private BasePanel GetPanel(UIPanelType panelType)
{
if (panelDict == null)
{
panelDict = new Dictionary();
}
//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().UIMng= this; //实例化UIManager
panelDict.Add(panelType, instPanel.GetComponent());
return instPanel.GetComponent();
}
else
{
return panel;
}
}
[Serializable]
class UIPanelTypeJson
{
public List infoList;
}
private void ParseUIPanelTypeJson()
{
panelPathDict = new Dictionarystring>();
TextAsset ta = Resources.Load("UIPanelType");
UIPanelTypeJson jsonObject = JsonUtility.FromJson(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);
}
}
///
/// just for test
///
//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;
///
/// 用于不同Manager之间的交互的
///
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);
}
}
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
通过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();
pwdInput = transform.Find("PasswordLabel/PasswordInput").GetComponent();
closeButton = transform.Find("CloseButton").GetComponent
服务器响应请求:
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);//向服务器发送请求的方法
}
这就是封装啊,登录请求继承请求基础类,请求调用发送方法,发送方法属于游戏底层的类,底层又归游戏管理类管理