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进行处理
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
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,数据的打包和数据的发送到客户端
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);
}
}
}
请看下集