基本客户端请求,服务端反馈实现
服务端

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 clientList = new List();
//所有房间
private List roomList = new 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("未找到对应处理");
    }        
}

}