基本客户端请求,服务端反馈实现
服务端
Server代码
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Net;
using Game_Socket_Server.Controller;
using GameSocketServerProtocol;
namespace Game_Socket_Server
{///
/// 服务器类
///
class Server
{
private Socket socket;
private UDPServer us;
//客户端列表
private List
//所有房间
private List
//控制类管理器
private ControllerManager controllerManager;
///
/// 服务端构造
///
/// 传入端口号
public Server(int prot)
{
controllerManager = new ControllerManager(this);
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Any, prot));//绑定端口
socket.Listen(10);//监听队列长度
StartAccept();//开支接收客户端连接
us = new UDPServer(6667, this, controllerManager);
}
void StartAccept()
{
//开始异步接收客户端Socket
socket.BeginAccept(AcceptCallBack,null);
}
void AcceptCallBack(IAsyncResult iar)
{
Socket client = socket.EndAccept(iar);//返回异步操作结果中接收到连入的客户端Socket
clientList.Add(new Client(client,this,us));//加入客户端List
StartAccept();//继续异步监听
}
public Client CientFromUserName(string user)
{
foreach (Client c in clientList)
{
if (c.GetUserInfo.Username == user)
return c;
}
return null;
}
/// <summary>
/// 处理客户端发来的请求
/// </summary>
/// <param name="pack">包</param>
/// <param name="client">客户端</param>
public void HandleRequest(MainPack pack,Client client)
{
//通过controllerManager找到对应的,请求控制类
controllerManager.HandleRequest(pack, client);
}
public MainPack CreateRoom(Client client,MainPack pack)
{
try
{
Room room = new Room(this, client, pack.Roompack[0]);
roomList.Add(room);
foreach (PlayerPack p in room.GetPlayerInfo())
{
pack.Playerpack.Add(p);
}
pack.Returncode = ReturnCode.Succeed;
return pack;
}catch
{
pack.Returncode = ReturnCode.Fall;
return pack;
}
}
public MainPack FindRoom()
{
MainPack pack = new MainPack();
pack.Actioncode = ActionCode.FindRoom;
try
{
if(roomList.Count==0)
{
pack.Returncode = ReturnCode.NotRoom;
return pack;
}
foreach (Room room in roomList)
{
pack.Roompack.Add(room.GetRoomInfo);
}
pack.Returncode = ReturnCode.Succeed;
}catch
{
pack.Returncode = ReturnCode.Fall;
}
return pack;
}
public MainPack JoinRoom(Client client,MainPack pack)
{
foreach (Room r in roomList)
{
//客户端加入str房间
if(r.GetRoomInfo.Roomname.Equals(pack.Str))
{
if(r.GetRoomInfo.State==0)
{
//房间可加入
r.Join(client);
pack.Roompack.Add(r.GetRoomInfo);
foreach (PlayerPack p in r.GetPlayerInfo())
{
pack.Playerpack.Add(p);
}
pack.Returncode = ReturnCode.Succeed;
return pack;
}else
{
//房间不可加入
pack.Returncode = ReturnCode.Fall;
return pack;
}
}
}
//当前无房间可加入
pack.Returncode = ReturnCode.NotRoom;
return pack;
}
public MainPack Exit(Client client,MainPack pack)
{
if(client.GetRoom==null)
{
pack.Returncode = ReturnCode.Fall;
return pack;
}
client.GetRoom.Exit(this, client);
pack.Returncode = ReturnCode.Succeed;
return pack;
}
public void Chat(Client client,MainPack pack)
{
pack.Str = client.UserName+":"+ pack.Str;
client.GetRoom.Broadcast(client, pack);
}
public void RemoveClient(Client client)
{
clientList.Remove(client);
}
public void RemoveRoom(Room room)
{
roomList.Remove(room);
}
}
}
服务端UDP转发类
using Game_Socket_Server.Controller;
using Game_Socket_Server.Tool;
using GameSocketServerProtocol;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Game_Socket_Server
{
class UDPServer
{
Socket udpServer;//UDPSocket
IPEndPoint bindEP;//本地监听IP
EndPoint remotEP;//远程IP
Server server;//TCPServer
ControllerManager controllerManager;//控制管理器调用对应事件防范
Byte[] buffer = new Byte[1024];//消息接收容器
Thread receiveThread;//接收线程
/// <summary>
/// UDP转发服务器构造
/// </summary>
/// <param name="port">监听端口号</param>
/// <param name="server">TCP服务器</param>
/// <param name="controllerManager">控制管理器</param>
public UDPServer(int port, Server server, ControllerManager controllerManager)
{
this.server = server;
this.controllerManager = controllerManager;
udpServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
bindEP = new IPEndPoint(IPAddress.Any, port);//监听本地IP端口
remotEP = (EndPoint)bindEP;//初始远程IP为默认本地,
udpServer.Bind(bindEP);
receiveThread = new Thread(ReceiveMag);
receiveThread.Start();
Console.WriteLine("UDP服务器以创建");
}
~UDPServer()
{
if (receiveThread != null)
{
receiveThread.Abort();
receiveThread = null;
}
}
/// <summary>
/// 服务器UDP接收端建立后即开始接收
/// </summary>
private void ReceiveMag()
{
while (true)
{
int len = udpServer.ReceiveFrom(buffer, ref remotEP);//UDP接收方法,数据写入buffer,返回另一端通信地址
MainPack pack = (MainPack)MainPack.Descriptor.Parser.ParseFrom(buffer, 0, len);
Handlerequest(pack, remotEP);
}
}
/// <summary>
/// UDP无连接,通过返回客户端地址进行通信
/// </summary>
/// <param name="buffer">接收客户端发来的数据</param>
/// <param name="remotEP">返回到到客户端地址</param>
/// <returns></returns>
public void Handlerequest(MainPack pack, EndPoint ipEndPoint)
{
Client client = server.CientFromUserName(pack.User);//通过包中的用户名找到对应的客户端类,对其通信地址赋值
if(client.IEP==null)
{
client.IEP = ipEndPoint;
}
controllerManager.HandleRequest(pack, client,true);
}
public void SendTo(MainPack pack,EndPoint point)
{
byte[] buff = Message.PackDateUDP(pack);
udpServer.SendTo(buff, buff.Length, SocketFlags.None, point);
}
}
}
ControllerManager代码
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using GameSocketServerProtocol;
namespace Game_Socket_Server.Controller
{
class ControllerManager
{
private Dictionary<RequestCode, BaseController> controlDict = new Dictionary<RequestCode, BaseController>();
private Server server;
public ControllerManager(Server server)
{
this.server = server;
UserController userController = new UserController();
controlDict.Add(userController.GetRequestCode, userController);
RoomController roomController = new RoomController();
controlDict.Add(roomController.GetRequestCode, roomController);
}
/// <summary>
/// 服务端消息处理方法,默认处理TCP消息
/// </summary>
/// <param name="pack"></param>
/// <param name="client"></param>
/// <param name="isUDP">是否为UDP数据报</param>
public void HandleRequest(MainPack pack,Client client,bool isUDP = false)
{
//根据Requestcode请求类型获取对应的请求类型控制器,如User-UserController
if (controlDict.TryGetValue(pack.Requestcode,out BaseController controller))
{
string mothooname = pack.Actioncode.ToString();//解析具体请求类型中的方法如:User中的Logon
//通过反射特性获取,处理该请求类型控制器中的方法,如UserController中的Logon
MethodInfo method = controller.GetType().GetMethod(mothooname);
if(method == null)
{
Console.WriteLine("没有找到对应的事件处理方法");
return;
}
//方法不为空,存在处理方法
Console.WriteLine("客户端"+mothooname+"请求");
object[] obj;
if (isUDP)
{
obj = new object[] { client, pack };
method.Invoke(controller, obj);//UDP只负责转发,不需要对客户端进行反馈
}
else
{
//方法的统一参数,(服务器Socket,客户端Socket,数据包MainPack)
obj = new object[] { server, client, pack };
//方法处理后返回的数据包
object ret = method.Invoke(controller, obj);//通过反射调用请求控制器中处理该请求的方法,返回处理后的的包
if (ret != null)//请求处理返回的数据包不为空(需要给客户端反馈时)
{
client.Send(ret as MainPack);//将处理结果发给客户端
}
//ret为空时即服务器对此请求不需要回复
}
}else
{
Console.WriteLine("没有找到对应的Ccontroller处理");
}
}
}
}
}
Client代码
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.Text;
using Game_Socket_Server.Tool;
using Game_Socket_Server.DAO;
using GameSocketServerProtocol;
namespace Game_Socket_Server
{
class Client
{
private Socket socket;//与远程客户端通信的Socket
private Message message;//消息类
private UserDate userDate;//用户数据类,连接数据库读取写入
private Server server;//服务器
private UDPServer us;//UDPServer
private EndPoint remoetEP;
//private int hp = 100;
public Room GetRoom//自动属性,客户端所在房间
{
get;set;
}
public string UserName//自动属性,用户名
{
get;set;
}
/public int HP
{
get { return hp; }
set { hp = value; }
}/
public UserInfo GetUserInfo
{
get;set;
}
public class UserInfo
{
public string Username
{
get;set;
}
public int Hp
{
get;
set;
}
public PosPack Pos
{
get;set;
}
}
public EndPoint IEP
{
get { return remoetEP; }
set { remoetEP = value; }
}
public UserDate GetUserDate//外部获取用户数据公开属性
{
get
{
return userDate;
}
}
public Client(Socket socket,Server server,UDPServer us)
{
this.server = server;
this.socket = socket;
this.us = us;
message = new Message();
userDate = new UserDate();
GetUserInfo = new UserInfo();
StartReceiveMsg();
}
/// <summary>
/// 客户端异步接收字节流数据
/// </summary>
void StartReceiveMsg()
{
//异步接收方法(用于接收的字节数组,开始接收位,可用于接收的长度,Sock标记,回调函数,null)
socket.BeginReceive(message.Buffer,message.StartIndex,message.Remsize,SocketFlags.None,ReceiveCallBak,null);
}
void ReceiveCallBak(IAsyncResult iar)
{
try
{
//如socket为空,或未连接,则返回
if (socket == null || socket.Connected == false) return;
//接收到的字节流长度
int len = socket.EndReceive(iar);
if (len == 0)//为空则返回
{
Close();
return;
}
//不为空时开始解析消息
message.ReadBuffer(len,HandleRequest);
StartReceiveMsg();
}
catch {
Close();
}
}
/// <summary>
/// 客户端发送数据方法
/// </summary>
/// <param name="pack">包转字节流</param>
public void Send(MainPack pack)
{
if (socket == null || socket.Connected == false) return;
try
{
socket.Send(Message.PackDate(pack));
}
catch
{ }
}
//UDP转发方法,UDP无连接,通过收到的客户端地址发送数据。
public void SendTo(MainPack pack)
{
if (IEP == null) return;
us.SendTo(pack, IEP);
}
//处理客户端发来的请求
private void HandleRequest(MainPack pack)
{
server.HandleRequest(pack, this);
}
private void Close()
{
Console.WriteLine("客户端断开");
if(GetRoom!=null)
{
GetRoom.Exit(server, this);
}
server.RemoveClient(this);
socket.Close();
}
}
}
Message处理类代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GameSocketServerProtocol;
using Google.Protobuf;
namespace Game_Socket_Server.Tool
{
///
/// 消息处理类,主要方法,将字节数组解析为MainPack包,将包转为字节数组。
///
class Message
{
///
/// 字节数组容器
///
private byte[] buffer = new byte[1024];
///
///
///
private int startIndex;
public byte[] Buffer
{
get
{
return buffer;
}
}
public int StartIndex
{
get
{
return startIndex;
}
}
public int Remsize
{
get
{
return buffer.Length - startIndex;
}
}
/// <summary>
/// 数据解析,字节流转为MainPack包
/// </summary>
/// <param name="len">读取的长度</param>
public void ReadBuffer(int len,Action<MainPack> HandleRequest)
{
startIndex += len;
while (true)//循环解析消息
{
if(startIndex<=4) return;//包头一个int类型长度<=4 包体无数据不需解析
int count = BitConverter.ToInt32(buffer,0);//读取4位返回一个int值
if (startIndex >= (count + 4))
{
//接收数据包
MainPack pack = (MainPack)MainPack.Descriptor.Parser.ParseFrom(buffer, 4, count);//byte[]容器,偏移(从第几位开始读),读多少位。
HandleRequest(pack);
//读取之后清理解析过的位
Array.Copy(buffer, count + 4, buffer, 0, startIndex - count - 4);
startIndex -= (count + 4);
}
else
{
break;
}
}
}
public static byte[] PackDate(MainPack pack)
{
byte[] date = pack.ToByteArray();//包体
byte[] head = BitConverter.GetBytes(date.Length);//包头
return head.Concat(date).ToArray();
}
//UDP MainPack打包不需要包头
public static byte[] PackDateUDP(MainPack pack)
{
return pack.ToByteArray();
}
}
}
MainPack,build之前代码(生成GameSocketServerProtocol.cs引入服务端/客户端使用)
syntax = "proto3";
package GameSocketServerProtocol;
//客户端请求类型枚举
enum RequestCode
{
RequestNone=0;
//用户请求
User = 1;
//房间请求
Room = 2;
//游戏请求
Game = 3;
}
//客户端请求事件类型
enum ActionCode
{
ActionNone=0;
//注册
Logon = 1;
//登录
Login = 2;
//创建房间
CreateRoom = 3;
//查询房间
FindRoom = 4;
//玩家列表:服务器发给客户端解析
PlayerList = 5;
//加入房间
JoinRoom = 6;
//离开
Exit = 7;
//聊天
Chat = 8;
//开始游戏
StartGame = 9;
//服务端发送来的开始游戏
Staring = 10;
//更新位置
UpState = 11;
//离开游戏
ExitGame = 12;
//更新角色和玩家列表
UpDatePlayerList = 13;
//开火
Fire = 14;
}
//服务器返回类型
enum ReturnCode
{
ReturnNone=0;
//成功
Succeed = 1;
//失败
Fall = 2;
//未找到房间
NotRoom = 3;
}
message MainPack
{
RequestCode requestcode=1;
ActionCode actioncode=2;
ReturnCode returncode=3;
//注册/登录信息包
LoginPack loginpack=4;
//防止发送空包占用空间的字符串,也可传输客户端聊天消息等
string str = 5;
//房间列表
repeated RoomPack roompack = 6;
//玩家列表
repeated PlayerPack playerpack = 7;
//子弹位置
BulletPack bulletpack = 8;
//用户名
string user = 9;
}
message LoginPack
{
//用户名
string username=1;
//密码
string password=2;
}
message RoomPack
{
//房间名
string roomname = 1;
//房间最大人数
int32 maxnum = 2;
//房间当前人数
int32 curnum = 3;
//房间状态
int32 state = 4;
}
message PlayerPack
{
//玩家名称
string playername = 1;
//玩家ID
string playerID = 2;
//玩家血量
int32 hp = 3;
//位置信息
PosPack pospack = 4;
}
message PosPack
{
//玩家2D坐标
float PosX = 1;
float PosY = 2;
float RotZ = 3;
float GunRotZ = 4;
}
message BulletPack
{
float PosX = 1;
float PosY = 2;
float RotZ = 3;
//鼠标位置
float MousePosX = 4;
float MousePosY = 5;
}
客户端代码
GameFace
using GameSocketServerProtocol;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameFace : MonoBehaviour
{
private ClientManeger clientManeger;
private RequestManager requestManager;
private UIManager uiManager;
PlayerManager playerManager;
public string UserName//客户端登录时赋值
{
get;set;
}
private static GameFace face;
public static GameFace Face
{
get
{
if (face == null)
face = GameObject.Find("GameFace").GetComponent<GameFace>();
return face;
}
}
// Start is called before the first frame update
void Start()
{
//face = this;
uiManager = new UIManager(this);
clientManeger = new ClientManeger(this);
requestManager = new RequestManager(this);
playerManager = new PlayerManager(this);
uiManager.OnInit();
clientManeger.OnInit();
requestManager.OnInit();
playerManager.OnInit();
}
private void OnDestroy()
{
clientManeger.OnDestory();
requestManager.OnDestory();
uiManager.OnDestory();
}
public void Send(MainPack pack)
{
clientManeger.Send(pack);
}
public void SendTo(MainPack pack)
{
pack.User = UserName;//向包中加入用户名,用于服务端UDP对本客户端通信地址赋值
clientManeger.SendTo(pack);
}
public void HandleResoonse(MainPack pack)
{
//处理
Debug.Log("服务器返回"+pack.Returncode.ToString());
requestManager.HandleResponse(pack);
}
public void AddRequest(BaseRequest request)
{
requestManager.AddRequest(request);
}
public void RemoveReqest(ActionCode action)
{
requestManager.RemoveRequest(action);
}
public void ShowMessage(string message,bool sync=false)
{
uiManager.ShowMessage(message,sync);
}
/*public void SetSelfID(string id)
{
playerManager.CurPlayerID = id;
}*/
public void AddPlayer(MainPack packs)
{
playerManager.AddPlayer(packs);
}
public void RemovePlayer(string id)
{
playerManager.RemovePlayer(id);
}
public void GameExit()
{
playerManager.GameExit();
uiManager.PopPaneal();
uiManager.PopPaneal();
requestManager.RemoveRequest(ActionCode.UpState);
requestManager.RemoveRequest(ActionCode.Fire);
}
public void UpPos(MainPack pack)
{
playerManager.UpPos(pack);
}
public void SpawnBullet(MainPack pack)
{
playerManager.SpawnBullet(pack);
}
}
BaseRequest基类
using GameSocketServerProtocol;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BaseRequest : MonoBehaviour
{
protected RequestCode requestCode;
protected ActionCode actionCode;
protected GameFace face;
public ActionCode GetActionCode
{
get { return actionCode; }
}
public virtual void Awake()
{
face = GameFace.Face;
}
public virtual void Start()
{
face.AddRequest(this);
Debug.Log(actionCode.ToString());
}
public virtual void OnDesttroy()
{
face.RemoveReqest(actionCode);
}
public virtual void OnResponse(MainPack pack)
{
}
//基类TCP发送
public virtual void SendRequest(MainPack pack)
{
face.Send(pack);
}
//基类UDP发送
public virtual void SendRequestUDP(MainPack pack)
{
face.SendTo(pack);
}
}
ClientManager
using GameSocketServerProtocol;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using UnityEngine;
public class ClientManeger : BaseManager
{
private Socket socket;
private Message message;
public ClientManeger(GameFace face) : base(face) { }
public override void OnInit()
{
base.OnInit();
message = new Message();
InitSocket();//初始化TCP通信
InitUDP();//初始化UDP通信
}
public override void OnDestory()
{
base.OnDestory();
message = null;
CloseSocket();
}/// <summary>
/// 初始化Socket
/// </summary>
private void InitSocket()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
socket.Connect("127.0.0.1", 6666);
//连接成功
face.ShowMessage("连接成功");
StartReceive();
}
catch(Exception e)
{
Debug.LogWarning(e);
//连接错误
face.ShowMessage("连接失败");
}
}
/// <summary>
/// 关闭Socket
/// </summary>
private void CloseSocket()
{
if(socket.Connected&&socket!=null)
{
socket.Close();
}
}
private void StartReceive()
{
socket.BeginReceive(message.Buffer, message.StartIndex, message.Remsize, SocketFlags.None, RecelveClientBack,null);
}
private void RecelveClientBack(IAsyncResult iar)
{
try
{
if (socket == null || socket.Connected == false) return;
int len = socket.EndReceive(iar);
if(len==0)
{
CloseSocket();
return;
}
message.ReadBuffer(len,HandleResoonse);
StartReceive();
}catch(Exception e)
{
Debug.Log(e.Message);
}
}
private void HandleResoonse(MainPack pack)
{
face.HandleResoonse(pack);
}
public void Send(MainPack pack)
{
socket.Send(Message.PackDate(pack));
}
//UDP通信**————————————————————————————**
private Socket udpClient;//Socket
private IPEndPoint iPEndPoint;//服务器地址
private EndPoint EPoint;//
private Byte[] buffer = new Byte[1024];
private void InitUDP()
{
udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
iPEndPoint = new IPEndPoint(IPAddress.Parse(ip), 6667);
EPoint = iPEndPoint;
try
{
udpClient.Connect(EPoint);
}
catch
{
Debug.Log("UPD连接失败");
return;
}
aucThread = new Thread(ReceiveMsg);
aucThread.Start();
}
private void ReceiveMsg()
{
//同步循环接收
Debug.Log("UDP开始接收");
while(true)
{
int len = udpClient.ReceiveFrom(buffer, ref EPoint);
MainPack pack = (MainPack)MainPack.Descriptor.Parser.ParseFrom(buffer, 0, len);
Debug.Log("接收数据" + pack.Actioncode + pack.User);
HandleResoonse(pack);//收到MainPack包的处理方法与TCP一致,
//通过RequestManager找到对应的Reeuest,调用对应UDP通信类型的处理方法(根据包中的数据同步位置/其他操作)
}
}
//UDP发送数据方法
public void SendTo(MainPack pack)
{
byte[] sendbuffer = Message.PackDateUDP(pack);
//服务端地址固定,首次连接后即可发送
udpClient.Send(sendbuffer, sendbuffer.Length, SocketFlags.None);//使用Send必须先连接“110”行代码
//SendTo需要目的地址EndPoint
}
}
RequestManager
using GameSocketServerProtocol;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RequestManager : BaseManager
{
public RequestManager(GameFace face):base(face) { }
private Dictionary<ActionCode, BaseRequest> requestDict = new Dictionary<ActionCode, BaseRequest>();
public void AddRequest(BaseRequest request)
{
requestDict.Add(request.GetActionCode, request);
Debug.Log("新请求:" + request.GetActionCode.ToString());
}
public void RemoveRequest(ActionCode action)
{
requestDict.Remove(action);
}
public void HandleResponse(MainPack pack)
{
if (requestDict.TryGetValue(pack.Actioncode, out BaseRequest request))
{
//基类回应请求方法,已在子类中重写
request.OnResponse(pack);
}else
{
Debug.Log("未找到对应处理");
}
}
}