【从零开始游戏开发】Unity 前后端网络通信该如何搭建?注释解答 | 全面总结 |建议收藏
你知道的越多,你不知道的越多 🇨🇳🇨🇳🇨🇳
点赞再看,养成习惯,别忘了一键三连哦 👍👍👍
文章持续更新中 📝📝📝
通信该如何搭建?
服务器端:
1. 入口类(Program):
static void Main(string[] args)
{
//1. 构造网络服务类:
NetServer net = new NetServer();
//2. 调用初始化方法:
net.Init();
//3. 调用开始方法:
net.Start();
//4. 死循环处理指令的输入
Run();
//5. 当循环跳出关闭服务器 调用停止服务方法
net.Stop();
}
public static void Run()
{
bool run = true;
while (run)
{
//1. 处理输入的指令 输入 Exit 指令会关闭服务器
}
}
2. 网络服务器类(Net Server):
public void Init()
{
//1. 绑定一个IP与端口号:
IPEndPoint ipPoint = new IPEndPoint(IP, Port);
//2. 构造一个监听类:
ServerListener = new TcpSocketListener(ipPoint);
//3. 给监听类绑定一个Socket连接事件:
ServerListener.SocketConnected += OnSocketConnected;
}
public void Start()
{
//1. 调用监听类开始方法:
Log.Warning("Starting Listener...");
ServerListener.Start();
//2. 调用消息分发器类开始函数
MessageDistributer<NetConnection<NetSession>>.Instance.Start(8);
Log.Warning("NetService Started");
}
public void Stop()
{
//1. 调用监听类停止方法
ServerListener.Stop();
}
//监听类监听到了对象连接,最后执行此方法
private void OnSocketConnected(object sender, Socket e)
{
//1. 获取连接的IP端地址
IPEndPoint clientIP = (IPEndPoint)e.RemoteEndPoint;
//2. 构造一个 连接模型
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
//3. 构造一个 NetSession
NetSession session = new NetSession();
//4. 构造一个 连接类 NetConnection<T>
NetConnection<NetSession> connection = new NetConnection<NetSession>()
}
//传递给连接类的接收数据事件
void DataReceived(NetConnection<NetSession> sender, DataEventArgs e)
{
//打印一条信息
Log.WarningFormat("Client[{0}] DataReceived Len:{1}", e.RemoteEndPoint, e.Length);
//1. 处理接收到的数据
lock (sender.packageHandler)
{
// 调用消息处理类的 接收数据方法
sender.packageHandler.ReceiveData(e.Data, 0, e.Data.Length);
}
}
//传递给连接类的断开连接事件
void Disconnected(NetConnection<NetSession> sender, SocketAsyncEventArgs e)
{
//打印一条信息
Log.WarningFormat("Client[{0}] Disconnected", e.RemoteEndPoint);
}
3. 监听类(TCP Socket Listener)
private Socket m_ListenSocket; //监听Socket
private SocketAsyncEventArgs args; //连接的一种模型
//构造函数
public TcpSocketListener(IPEndPoint endPoint)
{
//1. 实例化连接模型
args = new SocketAsyncEventArgs();
//2. 加上监听事件,到监听到有对象连接会执行
args.Completed += OnSocketAccepted;
}
public void Start()
{
lock (this) //加 Lock 是以为 此类会有多个客户端同时发起连接
{
//1. 初始化Socket 绑定终端 然后 开始监听:
m_ListenSocket.Listen(0);
//2. 监听到有连接执行:
BeginAccept(args);
}
}
public void Stop()
{
lock (this)
{
if (listenerSocket == null)
retutn;
//1. 关闭监听套接字
listenerSocket.Close();
listenerSocket = null;
}
}
private void BeginAccept(SocketAsyncEventArgs args)
{
//1. 制空 否则会报错
args.AcceptSocket = null;
//2. 得到监听到的对象
m_ListenSocket.AcceptAsync(args);
}
//监听事件
private void OnSocketAccepted(object sender, SocketAsyncEventArgs e)
{
//1. 获取或设置异步套接字操作的结果。
SocketError error = e.SocketError;
//容错处理
if (e.SocketError == SocketError.OperationAborted)
retutn;
//2. 连接成功
if (e.SocketError == SocketError.Success)
{
//1. 获取到连接的套接字:
Socket handler = e.AcceptSocket;
//2. 执行连接事件:
OnSocketConnected(handler);
}
lock (this)
{
BeginAccept(e); //达到循环监听
}
}
//连接事件
private void OnSocketConnected(Socket client)
{
//1. 执行连接事件:
SocketConnected?.Invoke(this, client);
}
//定义连接事件 网络服务器类初始化时 将此事件进行了赋值
public event EventHandler<Socket> SocketConnected;
//析构函数 结束时
~TcpSocketListener()
{
Dispose(false);
}
//此类继承了 IDisposable 实现接口
public void Dispose()
{
Dispose(true);
//请求公共语言运行时不要调用指定对象的终结器。手动调用了Dispose释放资源,那么析构函数就是不必要的了,这里阻止GC调用析构函数
GC.SuppressFinalize(this);
}
//处理结束函数 bool 参数,防止 析构函数清理资源一次 之后 Dispose 又一次清理
private void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
Stop();
if (args != null)
args.Dispose();
}
disposed = true;
}
}
4. 连接类(Net Connection <泛型类 T > )
/* 1. 对此类的介绍
每当监听类监听到有对象连接,执行了对应的事件,会构造一个当前类。
当前为泛型类 泛型 T 为 NetSession 类,此类缓存了连接对象的数据。
可以说是,每个连接的客户端都 对应了一个当前类,之后当前类会为客户端所服务。
*/
public class NetConnection<T>
{
// 定义 接收数据的回调 (委托)
public delegate void DataReceivedCallback(NetConnection<T> sender, DataEventArgs e);
// 定义 断开连接的回调 (委托)
public delegate void DisconnectedCallback(NetConnection<T> sender, SocketAsyncEventArgs e);
internal class State
{
public DataReceivedCallback dataReceived; //接收数据委托变量
public DisconnectedCallback disconnectedCallback; //断开连接委托变量
public Socket socket;
}
// 异步套接字操作
private SocketAsyncEventArgs eventArgs;
// 消息处理类对象
public PackageHandler<NetConnection<T>> packageHandler;
// 存储玩家数据类的变量
private T session;
public T Session { get { return session; } }
//构造函数
public NetConnection(Socket socket, SocketAsyncEventArgs args, DataReceivedCallback dataReceived,DisconnectedCallback disconnectedCallback, T session)
{
lock (this) // 因为类对 SocketAsyncEventArgs 此类进行操作时候 必须要加Lock 否则会出现报错
{
//构造一个 消息处理器
this.packageHandler = new PackageHandler<NetConnection<T>>(this);
State state = new State()
{
//1. 持有对构造方传递过来的值
socket = socket,
dataReceived = dataReceived,
disconnectedCallback = disconnectedCallback
}
//1. 初始化
eventArgs = new SocketAsyncEventArgs();
//2. 对 eventArgs 进行赋值
eventArgs.AcceptSocket = socket; //连接的Socket
eventArgs.Completed += ReceivedCompleted; //添加接收数据事件
eventArgs.UserToken = state; //将 对象 缓存
eventArgs.SetBuffer(new byte[64 * 1024],0, 64 * 1024); //设置 接收字节缓冲区
this.session = session; //将类缓存
//执行消息接收
BeginReceive(eventArgs);
}
}
//接收消息方法
private void BeginReceive(SocketAsyncEventArgs args)
{
lock (this)
{
//取得socket
Socket socket = (args.UserToken as State).socket;
//判断socket的连接状态
if (socket.Connected)
{
//开启异步请求,接收来自连接的数据
args.AcceptSocket.ReceiveAsync(args);
//当接收到了数据就会执行,此类构造时,赋值的接收数据事件
}
}
}
//接收数据事件
private void ReceivedCompleted(Object sender, SocketAsyncEventArgs args)
{
// 判断接收到的字节数
if (args.BytesTransferred == 0)
{
CloseConnection(args); //Graceful disconnect
return;
}
// 判断造成结果,是否成功
if (args.SocketError != SocketError.Success)
{
CloseConnection(args); //NOT graceful disconnect
return;
}
//1. 取得 对象 【这里又是对 UserToken 的应用】
State state = args.UserToken as State;
//2. 构建一个接收到的字节数长度的字节数组
Byte[] data = new Byte[args.BytesTransferred];
//3. 将 args 对象的字节数组内的数据 复制到 data
Array.Copy(args.Buffer, args.Offset, data, 0, data.Length);
//4. 执行接收到数据的事件
OnDataReceived(data, args.RemoteEndPoint as IPEndPoint, state.dataReceived);
//5. 达到循环接收
BeginReceive(args);
}
//调用接收数据事件的方法 事件是网络服务器类 构造此类是 传递来的
private void OnDataReceived(Byte[] data, IPEndPoint remoteEndPoint, DataReceivedCallback callback)
{
callback(this, new DataEventArgs() { RemoteEndPoint = remoteEndPoint, Data = data, Offset =0, Length = data.Length });
}
//断开连接的方法
private void CloseConnection(SocketAsyncEventArgs args)
{
//1. 从对象中获取到 Socket的对象 【这里就体现出 UserToken 这个属性的作用,当然不止这些】
State state = args.UserToken as State;
Socket socket = state.socket;
try
{
socket.Shutdown(SocketShutdown.Both); //禁止掉 Socket 的接收和发送
}catch { } // throws if client process has already closed
//2. 关闭socket
socket.Close();
socket = null;
//3. 去掉接收数据的事件,没有这步会出现报错
args.Completed -= ReceivedCompleted; //MUST Remember This!
//4. 执行断开连接的事件
OnDisconnected(args, state.disconnectedCallback);
}
//断开连接的事件,第二个参数 在网络服务器类构造此类时传递的事件方法
private void OnDisconnected(SocketAsyncEventArgs args, DisconnectedCallback callback)
{
callback(this, args);
}
}
5. 字节数组类(Data Event ARGs)
public class DataEventArgs : EventArgs
{
public IPEndPoint RemoteEndPoint { get; set; }
public Byte[] Data { get; set; }
public Int32 Offset { get; set; }
public Int32 Length { get; set; }
}
6. 消息包处理类(Package Handler)
public class PackageHandler<T>
{
// 字节数据缓冲区
private MemoryStream stream = new MemoryStream(64 * 1024);
//字节读取索引
private int readOffset = 0;
//接收 数据方法,调用者:网络服务器类
public void ReceiveData(byte[] data,int offset,int count)
{
//判断缓存区加上一定长度的数据会不会超过缓存的容量
if(stream.Position + count > stream.Capacity)
{
throw new Exception("PackageHandler write buffer overflow");
}
//1. 将数据写入到 缓冲区
stream.Write(data, offset, count);
//2. 解析消息包方法
ParsePackage();
}
//数据包解析方法
bool ParsePackage()
{
/*这里为什么要加 4,这个判断很关键
因为第一次接收 读取索引为0,加4可以判断,数据最基本的长度,如果这个都没有,那么这个包就没必要解析
*/
if (readOffset + 4 < stream.Position)
{
//1. 获取包内容的大小
int packageSize = BitConverter.ToInt32(stream.GetBuffer(), readOffset);
//2. 这里的 读取索引readOffset,感觉加没加都一样,根据整个方法代码,这个变量在这的作用为0
//但是这步判断很重要,包大小 加 4 如果小于等于成立,说明消息包是完整的,可解
if (packageSize + readOffset + 4 <= stream.Position)
{
//3. 调用通过字节提取消息类
Message.NetMessage message = UnpackMessage(stream.GetBuffer(), this.readOffset + 4, packageSize);
if (message == null)
{
throw new Exception("PackageHandler ParsePackage faild,invalid package");//容错处理
}
//4. 得到了 类,调用消息分发器传递该类,进行处理
MessageDistributer<T>.Instance.ReceiveMessage(this.sender, message);
//5. 将读取索引加上 整条完整数据长度
this.readOffset += (packageSize + 4);
//6. 返回这个类,是为了再次判断是否,有粘包,分包处理
return ParsePackage();
}
}
//当消息接收处理过一次,读取索引必定大于0
if (this.readOffset > 0)
{
//1. 这里是获取可能存在没有其他不完整的数据
long size = stream.Position - this.readOffset;
//2. 如果这个小于,说明缓冲区还有数据
if (this.readOffset < stream.Position)
{
Array.Copy(stream.GetBuffer(), this.readOffset, stream.GetBuffer(), 0, stream.Position - this.readOffset);
}
//3. Reset Stream
this.readOffset = 0;
stream.Position = size;
stream.SetLength(size);
}
return true;//结束
}
//提取消息类
public static Message.NetMessage UnpackMessage(byte[] packet,int offset,int length)
{
Message.NetMessage message = null;
using (MemoryStream ms = new MemoryStream(packet, offset, length))
{
message = ProtoBuf.Serializer.Deserialize<Message.NetMessage>(ms);
}
return message;
}
// -------- 客户端专用构造方式 --------
public class PackageHandler : PackageHandler<object>
{
public PackageHandler(object sender) : base(sender)
{
}
}
}
7. 消息分发器类(MessageDistributer)单例类
public class MessageDistributer<T> : Singleton<MessageDistributer<T>>
{
// 队列的消息类对象
class MessageArgs
{
public T sender;
public Message.NetMessage message;
}
// 消息类队列
private Queue<MessageArgs> messageQueue = new Queue<MessageArgs>();
// 自动重置事件,终止状态
private AutoResetEvent threadEvent = new AutoResetEvent(true);
// 线程执行状态
private bool Running = false;
// 开启的线程数量
public int ThreadCount = 0;
// 激活状态的线程数
public int ActiveThreadCount = 0;
// 处理消息包处理类传递的类 sender 是消息包处理类被构造时候,存放是 NetConnection<NetSeession> 对象,
public void ReceiveMessage(T sender ,Message.NetMessage message)
{
// 将消息添加到消息队列 MessageArgs
this.messageQueue.Enqueue(new MessageArgs() { sender = sender, message = message });
// 将事件状态设置为有信号,从而允许一个或多个等待线程继续执行。
threadEvent.Set();
}
// 多线程模式消息处理开始函数,参数为 工作线程的数量
public void Start(int ThreadNum)
{
this.ThreadCount = ThreadNum;
// 进行限制
if (this.ThreadCount < 1) this.ThreadCount = 1;
if (this.ThreadCount > 1000) this.ThreadCount = 1000;
// 激活线程执行状态
Running = true;
for (int i = 0; i < this.ThreadCount; i++)
{
// 调用 QueueUserWorkItem 方法以将方法排队以便在线程池线程上执行。 为此,可将方法传递给 WaitCallback 委托。 委托具有签名
ThreadPool.QueueUserWorkItem(new WaitCallback(MessageDistribute));
}
while (ActiveThreadCount < this.ThreadCount)
{
// 工作状态的线程 小于 总线程,进入休眠 具体作用不得而知 =-= 可能只是为了减轻CPU压力哦~
Thread.Sleep(100);
}
}
// 消息处理线程
private void MessageDistribute(Object stateInfo)
{
Log.Warning("MessageDistribute thread start");
try
{
// 如果不使用 Increment 和,则在 Decrement 执行前两个步骤后,线程可以被抢占
ActiveThreadCount = Interlocked.Increment(ref ActiveThreadCount);
while (Running)
{
if (this.messageQueue.Count == 0)
{
// 消息队列没有消息时 阻止当前线程,直到当前 WaitHandle 收到信号。
threadEvent.WaitOne();
continue;
}
// 取出队列消息
MessageArgs package = this.messageQueue.Dequeue();
if (package.message.Request != null)
{
// 请求消息不为空,执行请求事件消息发送 用于客户端~
MessageDispatch<T>.Instance.Dispatch(package.sender, package.message.Request);
}
if (package.message.Response != null)
{
// 响应消息不为空,执行请求事件消息发送 用于服务端~
MessageDispatch<T>.Instance.Dispatch(package.sender, package.message.Response);
}
}
catch{}
finally //必会执行的代码块 0.0 Increment Decrement,就和有生就有死一个道理
{
ActiveThreadCount = Interlocked.Decrement(ref ActiveThreadCount);
Log.Warning("MessageDistribute thread end");
}
}
}
// 停止多线程模式消息处理器
public void Stop()
{
Running = false;
this.messageQueue.Clear();
while (ActiveThreadCount > 0)
{
threadEvent.Set();
}
Thread.Sleep(100);
}
// 定义的委托
public delegate void MessageHandler<Tm>(T sender, Tm message);
// 委托字典
private Dictionary<string, System.Delegate> messageHandlers = new Dictionary<string, System.Delegate>();
// 就一个false 变量
public bool ThrowException = false;
// 消息派发类调用此方法,此方法也是每条通信数据最后的一处理,会执行谁订阅的方法
public void RaiseEvent<Tm>(T sender,Tm msg)
{
string key = msg.GetType().Name;
if (messageHandlers.ContainsKey(key))
{
// 在委托字典通过key 取得对应的委托
MessageHandler<Tm> handler = (MessageHandler<Tm>)messageHandlers[key];
if (handler != null)
{
try
{
handler(sender, msg);
}
catch (System.Exception ex)
{
Log.ErrorFormat("Message handler Fail");
// 也不知道这里要干嘛 =-=, 感觉没啥大作用
if (ThrowException)
throw ex;
}
}
else
{
Log.Warning("No handler subscribed for {0}" + msg.ToString());
}
}
}
// 订阅事件
public void Subscribe<Tm>(MessageHandler<Tm> messageHandler)
{
string type = typeof(Tm).Name;
// 如果有相同的,则把之前的制空,重新赋值
if (!messageHandlers.ContainsKey(type))
{
messageHandlers[type] = null;
}
messageHandlers[type] = (MessageHandler<Tm>)messageHandlers[type] + messageHandler;
}
// 有订阅 当然有注销
public void Unsubscribe<Tm>(MessageHandler<Tm> messageHandler)
{
string type = typeof(Tm).Name;
if (!messageHandlers.ContainsKey(type))
{
messageHandlers[type] = null;
}
messageHandlers[type] = (MessageHandler<Tm>)messageHandlers[type] - messageHandler;
}
//------- 下面区域是只有客户端才会用到的方法-------
public void Clear()
{
// 清空接收消息存储的容器
this.messageQueue.Clear();
}
}
8. 消息派发器类(MessageDispatch)单例类
// 在消息分发器类中,有订阅事件,此类只充当一个中介
public class MessageDispatch<T> : Singleton<MessageDispatch<T>>
{
// 响应派发 RaiseEvent 消息分发器类中处理
public void Dispatch(T sender, Message.NetMessageResponse message)
{
if (message.userRegister != null) { MessageDistributer<T>.Instance.RaiseEvent(sender, message.userRegister); }
}
// 请求派发
public void Dispatch(T sender, SkillBridge.Message.NetMessageRequest message)
{
if (message.userRegister != null) { MessageDistributer<T>.Instance.RaiseEvent(sender,message.userRegister); }
}
}
至此服务器端会用到的注释完毕
客户端:
网络类(NetClient)Mono单例类
class NetClient : MonoSingleton<NetClient>
{
private IPEndPoint address; // 终端
private Socket clientSocket;
public bool running { get; set; } // 网络服务器运行状态
private bool connecting = false; // 是否正在连接服务器
void Awake()
{
running = true;
}
// 初始化函数 率先绑定 终端
public void Init(string serverIP, int port)
{
this.address = new IPEndPoint(IPAddress.Parse(serverIP), port);
}
// 连接服务器函数
public void Connect(int times = DEF_TRY_CONNECT_TIMES)
{
if (this.connecting)
{
return; //当前在连接中,直接跳出
}
if (this.clientSocket != null)
{
//socket这东西,先关掉,在使用,每次使用前,先关掉,为什么呢? 如果你不做断线重连就可以忽略
this.clientSocket.Close();
}
if (this.address == default(IPEndPoint))
{
throw new Exception("Please Init first.");
}
this.connecting = true; // 正在连接
//真正执行连接
this.DoConnect();
}
// 开始连接
void DoConnect()
{
Debug.Log("NetClient.DoConnect on " + this.address.ToString());
try
{
if (this.clientSocket != null)
{
this.clientSocket.Close(); //解释过
}
this.clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this.clientSocket.Blocking = true; //开启阻塞
Debug.Log(string.Format("Connect[{0}] to server {1}", this.retryTimes, this.address) + "n");
//得到一个异步连接的结果~
IAsyncResult result = this.clientSocket.BeginConnect(this.address, null, null);
bool success = result.AsyncWaitHandle.WaitOne(1000);//1000 是连接等待结果的时间
if (success)
{
// success 为真,说明连接这档事成了, EndConnect 接收结果后,clientSocket 就可以发送接收消息了
this.clientSocket.EndConnect(result);
}
}
catch(SocketException ex)
{
// 连接被拒绝 为什么被拒绝,有点数的都知道被拉黑名单了 ( •̀ ω •́ )✧
if(ex.SocketErrorCode == SocketError.ConnectionRefused)
{
this.CloseConnection();
}
Debug.LogErrorFormat("DoConnect SocketException Fail");
}
catch (Exception e)
{
Debug.Log("DoConnect Exception:" + e.ToString() + "n");
}
// 判断是否连接成功
if (this.clientSocket.Connected)
{
this.clientSocket.Blocking = false; //关掉阻塞
//this.RaiseConnected(0, "Success"); 这条注释,是因为会执行一个事件,重连事件,属于功能性
}
else
{
// 进行重连的事情
}
this.connecting = false; //连接完毕
}
// 关闭连接 没有参数,因为参数只是打印出关闭的原因
public void CloseConnection()
{
this.connecting = false;
if (this.clientSocket != null)
{
this.clientSocket.Close();
}
//清空缓冲区
MessageDistributer.Instance.Clear(); //因为这个类是公用的,断线就需要清理,此类最下面有客户端专用的方法区域
this.sendQueue.Clear(); //清空发送消息队列
this.receiveBuffer.Position = 0; //接收消息的缓存区清0
this.sendBuffer.Position = sendOffset = 0; //发送的缓冲区 和发送索引 清0
}
// 需要发送的消息队列
private Queue<NetMessage> sendQueue = new Queue<NetMessage>();
// 发送数据的缓冲区
private MemoryStream sendBuffer = new MemoryStream();
// 接收数据的缓冲区
private MemoryStream receiveBuffer = new MemoryStream(64 * 1024);
// 这个类的构造,看此类最下方
public PackageHandler packageHandler = new PackageHandler(null);
//发送消息
public void SendMessage(NetMessage message)
{
if (!running)
{
return;
}
if (!this.Connected)
{
// 这里呢,就是你没了连接服务器 就调用此方法,这里会忽略掉你的请求,先连接服务器
this.receiveBuffer.Position = 0;
this.sendBuffer.Position = sendOffset = 0;
this.Connect();
Debug.Log("Connect Server before Send Message!");
return;
}
// 将消息添加到队列
sendQueue.Enqueue(message);
}
public void Update()
{
if (!running)
{
return; // 没有连接 跳出~
}
if (this.KeepConnect())
{
if (this.ProcessRecv())
{
// 保存是连接状态
if (this.Connected)
{
this.ProcessSend(); //发送过程
MessageDistributer.Instance.Distribute(); //ProceeMessage(); 方法只有这一条代码 直接写了,处理派发~
}
}
}
}
// 发送过程
bool ProcessSend()
{
bool ret = false;
try
{
if (this.clientSocket.Blocking)
{
Debug.Log("this.clientSocket.Blocking = truen");
}
bool error = this.clientSocket.Poll(0, SelectMode.SelectError);
if (error)
{
Debug.Log("ProcessSend Poll SelectErrorn");
this.CloseConnection();
return false;
}
// 检测是否可写,这个只要socket正常,就一定是真
ret = this.clientSocket.Poll(0, SelectMode.SelectWrite);
if (ret)
{
// 第一次肯定不大于因为都是0,执行完后,这里就大于了
if (this.sendBuffer.Position > this.sendOffset)
{
// 得到大小
int bufsize = (int)(this.sendBuffer.Position - this.sendOffset);
// 发送
int n = this.clientSocket.Send(this.sendBuffer.GetBuffer(), this.sendOffset, bufsize, SocketFlags.None);
if (n <= 0)
{
this.CloseConnection();
return false;
}
this.sendOffset += n;
if (this.sendOffset >= this.sendBuffer.Position)
{
//清空一定要做 this.sendOffset += n; if (this.sendOffset >= this.sendBuffer.Position) 其实可以不要
this.sendOffset = 0;
this.sendBuffer.Position = 0;
this.sendQueue.Dequeue();
}
}
else
{
if (this.sendQueue.Count > 0)
{
// 上面说 可以不要 ,是因为 这里,每次只求一条
NetMessage message = this.sendQueue.Peek();
byte[] package = PackageHandler.PackMessage(message);
// 写入数据
this.sendBuffer.Write(package, 0, package.Length);
}
}
}
}
}
// 接收消息的过程方法
bool ProcessRecv()
{
bool ret = false;
try
{
if (this.clientSocket.Blocking) //如果是阻塞的 就一直卡折,所以抛出个异常 提醒一下
{
Debug.Log("this.clientSocket.Blocking = truen");
}
bool error = this.clientSocket.Poll(0, SelectMode.SelectError);
if (error)
{
// 检测到错误 所以关闭
Debug.Log("ProcessRecv Poll SelectErrorn");
this.CloseConnection();
return false;
}
ret = this.clientSocket.Poll(0, SelectMode.SelectRead);
if (ret)
{
// 检测到有可读的消息
int n = this.clientSocket.Receive(this.receiveBuffer.GetBuffer(), 0, this.receiveBuffer.Capacity, SocketFlags.None);
if (n <= 0)
{
//接收到的长度为0.所以关闭,有消息过来大小为零的只有 关闭请求/响应 =-=
this.CloseConnection();
return false;
}
// 跟服务器端一样,接收数据的处理是一致的
this.packageHandler.ReceiveData(this.receiveBuffer.GetBuffer(), 0, n);
}
}
catch (Exception e)
{
// 接收过程异常~
Debug.Log("ProcessReceive exception:" + e.ToString() + "n");
this.CloseConnection();
return false;
}
return true;
}
// 状态判断把,比较Update 一直在执行
bool KeepConnect()
{
if (this.connecting)
return false;
if (this.address == null)
return false;
if (this.Connected)
return true;
//if (this.retryTimes < this.retryTimesTotal) this.Connect(); //这个还是断线重连的处理
return false;
}
}
🎁🌻🌼🌸 粉丝福利来喽 🎁🌻🌼🌸
- 免费领取海量资源 🎁
简历自测评分表、Unity职级技能表、面试题库、入行学习路径等- 《Unity游戏开发五天集训营 》50个名额 🎁
我给大家争取到了 50个《游戏开发五天集训营 》名额,原价198,前50个免费
扫码加入,暗号小听歌
即可参加ARPG狼人战斗系统、饥荒生存类游戏开发、回合制RPG口袋妖怪游戏等游戏开发训练营- 额外抽奖机会🎁
参加游戏训练营、还有机会获得大厂老师在线面试指导、或者有机会获得价值1998元的《Unity极速入门与实战》课程
🔻🔻🔻🔻
扫下方二维码,获取游戏开发福利,暗号小听歌 🔻🔻🔻🔻
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
二维码