上层协议收发和心跳包
我们在之前写的NetManager,属于消息发送的底层工具类,它提供各种消息的收发接口,协议收发需要另外一个单独的工具类。
ProtocolManager在Unity工程的Scripts——Net文件夹里,新建ProtocolManager
我们先实现一下获取私钥的协议
1234567891011121314151617public class ProtocolManager { /// <summary> /// 请求私钥 /// </summary> public static void SecretRequest() { MsgSecret msg = new MsgSecret(); NetManager.Instance.AddProtoListener(ProtocolEnum.MsgSecret, (secMsg) => { MsgSecret msgSecret = secMsg as MsgSecret; ...
客户端发送协议
客户端发送消息的时候,也是异步发送,所以和服务器直接发送不一样,需要先将bytes数据都包装在ByteArray里。
在ByteArray内添加新的构造函数
12345678public ByteArray(byte[] defaultBytes){ Bytes = defaultBytes; Capacity = defaultBytes.Length; m_InitSize = defaultBytes.Length; ReadIdx = 0; WriteIdx = defaultBytes.Length;}
Capacity = defaultBytes.Length; WriteIdx = defaultBytes.Length;表示构造的ByteArray并不是空的,是一个可读不可写的状态,因为此时它的Remain(Capacity - WriteIdx)等于0
在客户端NetManager中声明一个Queue,把需要发送的ByteArray都放在这个Queue里,然后将Queue中的ByteArray一个一个发送出去。 ...
客户端处理消息
在上一节我们将消息都添加到了列表当中,这一节我们将处理列表中的消息。
一般情况下,列表中的消息分为两类,一类是需要游戏前台处理的消息,一类是需要后台处理的。后台处理的消息包括心跳包等,因为这类消息当玩家不玩游戏转到别的软件时需要持续处理。
我们在NetManager里面开启两个线程,一个是消息处理线程,一个是心跳包线程,消息处理线程负责区分前台消息和后台消息。
12private Thread m_MsgThread;private Thread m_HeartThread;
消息处理线程在客户端成功连接服务器时,初始化m_MsgThread,所以在ConnectCallback中添加:
12345678910111213141516171819202122/// <summary>/// 链接回调/// </summary>/// <param name="ar">回调的状态</param>void ConnectCallback(IAsyncResult ar){ try { ...
客户端关闭连接和解析数据
客户端关闭连接在NetManager中添加CloseConnection方法
1234567891011121314151617181920212223242526272829void ReceiveCallback(IAsyncResult ar){ try { Socket socket = (Socket)ar.AsyncState; int count = socket.EndReceive(ar); if(count <= 0) { //说明服务器已经关闭链接 CloseConnection();//+++ return; } //...} /// <summary> /// 关闭与远程服务器的链接 /// </summary> /// <param name="normal">是否 ...
客户端链接和接收
我们先将一些服务器上面的代码转移到之前的ResLoadPrg项目里。服务器和客户端不同,不要让客户端依赖服务器的dll库。反过来可以。在开发阶段可以将一些公用的代码放到客户端里,然后使用Unity的Assembly Definition生成一个独立的dll库来让服务端的工程依赖上。
首先在Script文件夹里新建Net文件夹,将服务器工程的ByteArray.cs粘贴过来,在Net文件夹内新建Proto文件夹,将服务器工程的MsgBase.cs、MsgSecret.cs、ProtocolEnum.cs粘贴过来。
将MsgBase内添加上一些命名空间,并把剩下的报错注释掉
1234using ProtoBuf;using System;using UnityEngine;using System.IO;
给ByteArray添加命名空间
1using System;
如果想要消除一些CS8632警告,将相关可空类型的声明的上下文添加#nullable enable 和#nullable disable
如果是新工程,参考热更教程里面接入protobuf的方法,并且将服务器工程的Sin ...
服务器收发数据
发送数据在ServerSocket中添加发送数据的方法:
1234567891011121314151617181920212223242526272829303132333435/// <summary>/// 发送数据/// </summary>/// <param name="client">发送给的客户端</param>/// <param name="data">消息</param>public static void Send(ClientSocket client, MsgBase msg){ if (client.Socket == null) return; if (client == null || !client.Socket.Connected) return; try { byte[] nameBytes = MsgBase.EncodeName(msg); byte[] bod ...
通信协议
我们直接使用序列化后的ProtoBuff数据来通信,并且加密数据。
我们只使用简易的序列化,不使用.proto中间文件来映射类。
每个和客户端通信的协议都是一个类。具体到传输时,传输的协议内容分为三个部分,第一个部分是协议头部分,记录了整段信息的大小,第二个部分是协议名部分,协议名部分包含了协议名长度和具体的协议名,第三个部分才是具体的协议内容。
ProtocolEnum使用一个枚举来表示协议类型。协议在传输时就是用一个数字表示的。
在Proto文件夹内新建ProtocolEnum文件
123456public enum ProtocolEnum{ None = 0, MsgSecret = 1,//密钥协议 MsgPing = 2,//心跳包协议}
MsgBase首先在Nuget包管理器中将ProtoBuf-net安装到SimpleServer工程内。
在Proto文件夹内新建MsgBase文件,这个是各种协议的基类,还提供了协议内容编解码的方法。
EncodeName方法,将协议名编码,将协议名按照UTF8编码的形式转化成byte数组之后 ...
ByteArray(难点)
ByteArrayByteArray封装了和一个客户端进行通信时的byte数据处理操作集合。
ByteArray循环利用初始的byte数组,初始值为1024byte。
当写入的数据大于1024byte,自动扩容。
ByteArray属于常用的工具类,在实际使用中根据情况可以使用ObjectPoolManager进行优化。这里先略
在Net文件夹中添加ByteArray类
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788namespace SimpleServer.Net{ public class ByteArray { //默认byte[]大小 public const int DEFAULT_SIZE = 1024; //初始大 ...
多路复用Select和心跳包
想要使用多路复用Select,就必须让服务器连接的Socket都交给Select处理。
服务器获取客户端Socket首先,在Net文件夹新建一个ClientSocket类:
12345678910using System.Net.Sockets;namespace SimpleServer.Net{ public class ClientSocket { public Socket? Socket { get; set; } public long LastPingTime { get; set; } }}
然后,在ServerSocket声明一个字典,用来缓存所有连接到服务器的客户端Socket
1234//所有客户端的字典public static Dictionary<Socket,ClientSocket> m_ClientDic = new Dictionary<Socket,ClientSocket>();//临时保存所 ...
服务器准备和log4net
ServerSocket服务器与客户端建立连接的流程
在SimpleServer的Net文件夹内新建ServerSocket:
1234567891011121314151617181920212223242526272829303132333435using System.Net.Sockets;using System.Net;namespace SimpleServer.Net{ internal class ServerSocket : SingletonPattern<ServerSocket> { //公钥 public static string PublicKey = "ATAOServer"; //密钥,后续可以随时间进行变化 public static string SecretKey = "ATAO_UP&&jiiEf=Ted";#if DEBUG private string m_IPAddr ...