java--游戏后端--项目开发总结9--客户端--底层协议
Posted plxz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java--游戏后端--项目开发总结9--客户端--底层协议相关的知识,希望对你有一定的参考价值。
- 建立TCP连接
- 另起线程接收服务端消息
- 根据自己的消息设计规则进行消息的解码
- 解码出协议号
- 解码出参数
- 参数解码成具体的参数类
- 服务端主动推送的需要先注册方法
- 在保存表的协议字典中找出对应的表
- 调用回调方法
- 发送消息给服务端
- 将发送消息的lua表保存起来
- 组装消息发送给服务端
- 本底层为了扩展方便,使用了唯一的回调参数,此参数可进行任意变化与组合
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--客户端--底层协议的主要内容,如果未能解决你的问题,请参考以下文章