java--游戏后端--项目开发总结9--客户端--底层协议

Posted plxz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java--游戏后端--项目开发总结9--客户端--底层协议相关的知识,希望对你有一定的参考价值。

  1. 建立TCP连接
  2. 另起线程接收服务端消息
    1. 根据自己的消息设计规则进行消息的解码
    2. 解码出协议号
    3. 解码出参数
    4. 参数解码成具体的参数类
    5. 服务端主动推送的需要先注册方法
    6. 在保存表的协议字典中找出对应的表
    7. 调用回调方法
  3. 发送消息给服务端
    1. 将发送消息的lua表保存起来
    2. 组装消息发送给服务端
  4. 本底层为了扩展方便,使用了唯一的回调参数,此参数可进行任意变化与组合
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;
using XLua;

[LuaCallCSharp]
public class Client : MonoBehaviour {
    private const int TIMEOUT = 5000;//连接超时
    private Socket socket;//socket连接
    private Dictionary<int, LuaTable> responseCommandMap = new Dictionary<int, LuaTable>();//协议对象
    private const string RESPONSE_METHOD_NAME = "Response";//回调方法名称

    //连接服务器
    public void ConnectServe(string ip, int port) {
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//采用TCP方式连接
        IPAddress address = IPAddress.Parse(ip);//服务器IP地址
        IPEndPoint endpoint = new IPEndPoint(address, port);//服务器端口
        IAsyncResult result = socket.BeginConnect(endpoint, new AsyncCallback(ConnectCallback), socket);//异步连接,连接成功调用connectCallback方法
        bool success = result.AsyncWaitHandle.WaitOne(TIMEOUT, true);//连接超时的监测,当连接超过5秒还没成功表示超时
        if (!success) {
            Closed();//超时
            Debug.Log("连接超时");
        } else {
            //与socket建立连接成功,开启线程接受服务端数据。
            Thread thread = new Thread(new ThreadStart(ReceiveSorket));
            thread.IsBackground = true;
            thread.Start();
        }
    }

    //连接服务器回调
    private void ConnectCallback(IAsyncResult asyncConnect) {
    }

    //接收服务器消息
    private void ReceiveSorket() {
        //在这个线程中接受服务器返回的数据
        while (true) {
            if (!socket.Connected) {
                //与服务器断开连接跳出循环
                Debug.Log("与服务端断开连接");
                socket.Close();
                break;
            }
            try {
                //接受数据保存至bytes当中
                byte[] bytes = new byte[4096];
                //Receive方法中会一直等待服务端回发消息
                //如果没有回发会一直在这里等着。
                int i = socket.Receive(bytes);
                if (i <= 0) {
                    socket.Close();
                    break;
                }

                int length = 0;

                //协议号长度
                byte[] protocolIdLengthByte = new byte[4];
                Buffer.BlockCopy(bytes, length, protocolIdLengthByte, 0, protocolIdLengthByte.Length);
                length += protocolIdLengthByte.Length;
                int protocolIdLength = ByteUtil.Bytes2Int(protocolIdLengthByte);

                //协议号
                byte[] protocolIdByte = new byte[protocolIdLength];
                Buffer.BlockCopy(bytes, length, protocolIdByte, 0, protocolIdByte.Length);
                length += protocolIdByte.Length;
                int protocolId = ByteUtil.Bytes2Int(protocolIdByte);

                //参数长度
                byte[] parameterLengthByte = new byte[4];
                Buffer.BlockCopy(bytes, length, parameterLengthByte, 0, parameterLengthByte.Length);
                length += parameterLengthByte.Length;
                int parameterLength = ByteUtil.Bytes2Int(parameterLengthByte);

                //参数
                byte[] parameterByte = new byte[parameterLength];
                Buffer.BlockCopy(bytes, length, parameterByte, 0, parameterByte.Length);

                LoomUtil.QueueOnMainThread(() => {
                    LuaTable table;
                    responseCommandMap.TryGetValue(protocolId, out table);
                    if (null != table) {
                        table.Get<LuaFunction>(RESPONSE_METHOD_NAME).Call(table, ProtobufSerializerUtil.Deserialize<ParamList>(parameterByte));
                    } else {
                        Debug.LogError("未定义的协议号 = " + protocolId);
                    }
                });
            } catch (Exception e) {
                Debug.Log("连接中断" + e);
                socket.Close();
                break;
            }
        }
    }

    //关闭Socket
    private void Closed() {
        if (socket != null && socket.Connected) {
            socket.Shutdown(SocketShutdown.Both);
            socket.Close();
        }
        socket = null;
    }

    //退出程序
    void OnDestroy() {
        Closed();
        Debug.Log("关闭客户端,断开连接");
    }

    //向服务端发送数据
    public void SendRequest(string tableName, int protocolId, object obj) {
        //保存请求对象
        if (!responseCommandMap.ContainsKey(protocolId)) {
            string[] sArray = tableName.Split(new char[1] { / });
            responseCommandMap.Add(protocolId, LuaBehaviour.luaEnv.Global.Get<LuaTable>(sArray[sArray.Length - 1]));
        }

        List<byte[]> list = new List<byte[]>();

        //协议号
        byte[] protocolIdByte = ByteUtil.Int2Byte(protocolId);

        //协议号长度
        byte[] protocolIdLengthByte = new byte[4];
        protocolIdLengthByte = ByteUtil.Int2Byte(protocolIdByte.Length);

        //参数
        byte[] parameterByte = ProtobufSerializerUtil.Serialize(obj);
        //参数长度
        byte[] parameterLengthByte = new byte[4];
        parameterLengthByte = ByteUtil.Int2Byte(parameterByte.Length);

        //组装好的消息
        list.Add(protocolIdLengthByte);
        list.Add(protocolIdByte);
        list.Add(parameterLengthByte);
        list.Add(parameterByte);

        byte[] msg = ByteUtil.MergeByte(list);

        if (!socket.Connected) {
            socket.Close();
            Debug.Log("服务器断开连接");
            return;
        }
        try {
            IAsyncResult asyncSend = socket.BeginSend(msg, 0, msg.Length, SocketFlags.None, new AsyncCallback(SendCallback), socket);
            bool success = asyncSend.AsyncWaitHandle.WaitOne(TIMEOUT, true);
            if (!success) {
                socket.Close();
                Debug.Log("发送失败");
            }
        } catch {
            Debug.Log("发送错误");
        }
    }

    //注册回调方法,此方法适用于服务端主动推送的协议
    public void RegisterResponse(string tableName, int protocolId) {
        //保存请求对象
        if (!responseCommandMap.ContainsKey(protocolId)) {
            string[] sArray = tableName.Split(new char[1] { / });
            responseCommandMap.Add(protocolId, LuaBehaviour.luaEnv.Global.Get<LuaTable>(sArray[sArray.Length - 1]));
        }
    }

    //发送回调
    private void SendCallback(IAsyncResult asyncConnect) {
    }
}

以上是关于java--游戏后端--项目开发总结9--客户端--底层协议的主要内容,如果未能解决你的问题,请参考以下文章

毕业之后所有面试总结

毕业之后所有面试总结

毕业之后所有面试总结

Java 后端工程师实习总结

毕业之后所有面试总结

二食堂Alpha - 项目展示