【从零开始游戏开发】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;
    }
}

🎁🌻🌼🌸 粉丝福利来喽 🎁🌻🌼🌸

  1. 免费领取海量资源 🎁
    简历自测评分表、Unity职级技能表、面试题库、入行学习路径等
  2. 《Unity游戏开发五天集训营 》50个名额 🎁
    我给大家争取到了 50个《游戏开发五天集训营 》名额,原价198,前50个免费
    扫码加入,暗号小听歌
    即可参加ARPG狼人战斗系统、饥荒生存类游戏开发、回合制RPG口袋妖怪游戏等游戏开发训练营
  3. 额外抽奖机会🎁
    参加游戏训练营、还有机会获得大厂老师在线面试指导、或者有机会获得价值1998元的《Unity极速入门与实战》课程

🔻🔻🔻🔻
扫下方二维码,获取游戏开发福利,暗号小听歌 🔻🔻🔻🔻

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>