制定通信协议

2023-05-16

                                                                                                           

                                               制定通信协议  

什么是制定通信协议?

    客户端在和服务器进行通信的时候,为了让双方都能辨别接收到的消息的内容,由发送方和接收方而制定的相关约定。

为什么制定通信协议?

在大型的网络游戏中,客户端和服务器发送的消息会很多很多,我们为了方便管理和区分这些消息,所以需要制定一些协议。比如说游戏登陆的时候,需要向服务器发送登陆的信息,我们把信息封装在登陆协议里面,直接把登陆协议发送给服务器。在做同步的时候,需要向服务器发送位置的信息,直接把同步协议发送给服务器,服务器可以通过协议的编号,来判断收到的协议是哪种协议,从而获取到相关的信息。

通信协议一般在哪里会用到?

    1.项目中各个模块之间传递消息

2. 客户端和服务端之间进行通信

如何去制定通信协议?

   1.为了方便区分每个协议,所以每个协议可以有一个唯一标识,我把它定位协议编号。

    所以我把它定义成属性放接口里面,每个协议都得去实现这个属性。

/// <summary>

/// 协议接口

/// </summary>

public interface  IProto {

 

/// <summary>

///协议编号

/// </summary>

 ushort ProtoID{get;}

}

  2.由于每个协议的内容肯定不一样,所以我们把内容放在具体协议类中,由于我用的是socket TCP通信,我的客户端和服务器制定的发送方法和接收方法参数是byte[] buffer数组,为了方便上层客户端调用,所以我们可以在具体协议类中加两个方法,第一方法是为了发送方便,第二个静态方法是为了接收方便。下面我通过一个具体协议来举例说明。

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

/// <summary>

/// 获取邮件详情

/// </summary>

public struct Mail_Get_DetailProto:IProto  {

 

    //此处编号为方便调用,特放在枚举里面

/// <summary>

/// 协议编号

/// </summary>

/// <value>The proto I.</value>

public ushort ProtoID{get{ return ProtoIDDef.Mail_Get_Proto;}}

 

/// <summary>

/// 是否发送成功

/// </summary>

public bool IsSuccess;

 

/// <summary>

/// 邮件名称

/// </summary>

public string Name;

 

/// <summary>

/// 错误编码

/// </summary>

public ushort ErrorCode;

 

/// <summary>

///转成流数组

/// </summary>

/// <returns>The array.</returns>

public byte[] ToArray()

{

using (MMO_MemoryStream ms = new MMO_MemoryStream ()) {

ms.WriteUShort (ProtoID);

ms.WriteBool (IsSuccess);

if (IsSuccess) {

ms.WriteUTF8String (Name);

} else {

ms.WriteUShort (ErrorCode);

}

return ms.ToArray ();

}

}

 

/// <summary>

///获取协议

/// </summary>

/// <returns>The proto.</returns>

/// <param name="buffer">Buffer.</param>

public static   Mail_Get_DetailProto GetProto(byte[] buffer)

{

Mail_Get_DetailProto proto = new Mail_Get_DetailProto ();

using (MMO_MemoryStream ms = new MMO_MemoryStream (buffer)) {

proto.IsSuccess = ms.ReadBool ();

if (proto.IsSuccess) {

proto.Name = ms.ReadUTF8String ();

} else {

proto.ErrorCode = ms.ReadUShort ();

}

}

return proto;

}

}

3. 基于Http的通信协议制定

做弱联网游戏或者模块通信的时候呢,一般呢是采用Http的,比如说做Web服务器项目和客户端Unity项目通信的时候,由于底层已经封装好,参数是json格式的字符串,所以我们的协议还得序列化成json格式,接收到的时候,把json对象反序列化成我们的数据对象。

 

通过项目中实际代码说明一下吧,下面是客户端的注册逻辑过程。

 /// <summary>

    /// 注册窗口

    /// </summary>

    private void OnBtnRegisterClick()

    {

        //注册逻辑TODO

        nickName = m_InputeNickName.text.Trim();

        string pwd = m_InputNickPassword.text.Trim();

        string pwd2 = m_InputReNickPassword.text.Trim();

        if(string.IsNullOrEmpty(nickName))

        {

            m_TextTip.text = "提示:请输入账号!";

            return;

        }

 

        if (string.IsNullOrEmpty(pwd))

        {

            m_TextTip.text = "提示:请输入密码!";

            return;

        }

        if(string.IsNullOrEmpty(pwd2))

        {

            m_TextTip.text = "提示:请输入确认密码!";

            return;

        }

        if(pwd!=pwd2)

        {

            m_TextTip.text = "提示:两次输入密码不一致!";

            return;

        }

        if(nickName.Length<8)

        {

            m_TextTip.text = "提示:昵称最少为8个有效字符";

            return;

        }

        else if(nickName.Length>16)

        {

            m_TextTip.text = "提示:昵称最多为16个有效字符";

            return;

        }

        if (pwd.Length < 8)

        {

            m_TextTip.text = "提示:密码最少为8个有效字符";

            return;

        }

        else if (pwd.Length > 16)

        {

            m_TextTip.text = "提示:密码最多为16个有效字符";

            return;

        }

        if (!string.IsNullOrEmpty(nickName)&&!string.IsNullOrEmpty(pwd))

        {

//把数据发送到服务器

JsonData jsonData=new JsonData();

jsonData ["Type"] = 1;

jsonData ["UserName"] = nickName;

jsonData ["Pwd"] = pwd;

             //把消息提交到web服务器

NetWorkHTTP.Instance.SendData (GlobalInit.WebAccountUrl + "api/Account", PostCallBack, true, jsonData.ToJson ());

}

       else

        {

            m_TextTip.text = "提示:请输入账号或密码!";

            return;

        }

    }

   //提交消息回调方法

private void PostCallBack(NetWorkHTTP.CallBackArgs obj)

{

if (obj.isError)

{

Debug.Log(obj.Error);

}

else

{

 

RetValue ret = JsonMapper.ToObject<RetValue>(obj.Json);

 

            if (ret.RetData == "注册成功")

            {

                JsonData jsonData = new JsonData();

                jsonData["Type"] = 2;

                jsonData["UserName"] = nickName;

                jsonData["LoginTime"] = DateTime.Now.ToString();

                jsonData["IsOnLine"] = 1;

                NetWorkHTTP.Instance.SendData(GlobalInit.WebAccountUrl + "api/Account", PostCallBack, true, jsonData.ToJson());

                GameSceneManager.Instance.LoadGameScene(SceneType.Scene_MainCity);

            }

            else

            {

                Debug.Log(ret.RetData);

                m_TextTip.text = ret.RetData;

            }

 

        }

}

 

下面是Web服务器对接收到的消息进行处理

 

        // POST: api/Account

        public RetValue Post([FromBody]string jsonStr)

        {

            RetValue ret = new RetValue();

            try

            {

                JsonData jsonData = JsonMapper.ToObject<JsonData>(jsonStr);

 

                int type = int.Parse(jsonData["Type"].ToString());

 

                if (type == 0)

                {

                    //登陆协议

                    string userName = jsonData["UserName"].ToString();

                    string pwd = jsonData["Pwd"].ToString();

                    //登陆 去数据库里面进行查找,比较用户名和密码是否相等

 

                    AccountData account = DBManager.GetUserByUsername(userName);

                    //如果数据表为空,则此用户名不存在

                    if (account == null)

                    {

                        //发送 此用户不存在

                        ret.RetData = "此账号不存在";

 

                    }

                    //否则比较密码是否一致

                    else

                    {

                        //先验证密码是否正确,如果正确,再去验证是否在线

                        if (account.Pwd == pwd && account.IsOnLine == 0)

                        {

                            //可以登陆

                            ret.HasError = false;

                            ret.RetData = "账号密码正确";

 

                        }

                        else if (account.Pwd == pwd && account.IsOnLine == 1)

                        {

                            //此用户已在线

                            ret.HasError = false;

                            ret.RetData = "此用户已经在线";

 

                        }

                        else

                        {

                            //发送密码不正确

                            ret.HasError = false;

                            ret.RetData = "账号密码不匹配,请重新登录";

                        }

                    }

                }

                else if (type == 1)

                {

                    //注册协议

                    string userName = jsonData["UserName"].ToString();

                    string pwd = jsonData["Pwd"].ToString();

                    //先去数据库里面查询是否有相同的用户名,如果有的话,返回此账号已经存在

                    AccountData accountTemp = DBManager.GetUserByUsername(userName);

                    if (accountTemp != null)

                    {

                            ret.HasError = false;

                            ret.RetData = "此账号已经存在";

     

                    }

                    else 

                    {

                        AccountData account = new AccountData();

                        account.UserName = userName;

                        account.Pwd = pwd;

                        account.CreateTime = DateTime.Now;

                        DBManager.SaveUser(account);

                        ret.HasError = false;

                        ret.RetData = "注册成功";

                    }

                }

                else if (type == 2)

                {

                    //登陆信息协议

                    string userName = jsonData["UserName"].ToString();

                    DateTime loginTime = Convert.ToDateTime(jsonData["LoginTime"].ToString());

                    int isOnLine =Convert.ToInt32( jsonData["IsOnLine"].ToString());

                    AccountData account = DBManager.GetUserByUsername(userName);

                    account.UserName = userName;

                    account.LoginTime = loginTime;

                    account.IsOnLine = isOnLine;

                    DBManager.UpdateUserByName("t_account1", new string[] { "LoginTime", "IsOnLine" }, new string[] { loginTime.ToString("yyyyMMddHHmmss"), isOnLine.ToString() }, "UserName", userName);

                }

            }

            catch (Exception e)

            {

                ret.HasError = true;

                ret.Error = e.Message;

              

            }

            return ret;

 

        }

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

制定通信协议 的相关文章

  • VSCode 常用设置项

    代码编辑工具VSCode 常用设置项 span class token punctuation span span class token comment VScode主题配置 span span class token string 34
  • 机器人运动控制-上位机通讯

    机器人 xff0c 无论是工业机器人还是服务机器人等多种类机器人 xff0c 都有自己的控制器 在他们的控制面板上 xff0c 我们可以通过简单的操作和程序指令 xff0c 让机器人自行运动 为了让机器人更加智能 xff0c 我们需要在机器
  • Imu误差模型、零偏、零偏稳定性

    原文链接 零偏 xff0c 零偏稳定性和零偏重复性 xff0c IMU误差模型 什么是零偏 xff08 Bias xff09 在陀螺静止时 xff0c 陀螺仪仍会 xff0c 以规定时间内测得的输出量平均值相应的等效输入角速率表示 xff0
  • 海思3516a实现OSD叠加水印

    文章目录 前言一 三个文件的编译二 海思SDK使用步骤1 创建叠加字符2 添加叠加区域到视频通道 总结 前言 两天的努力终于实现了 xff0c 激动 xff01 在网上查阅了各种资料 xff0c 只是有零散的信息 xff0c 海思3516a
  • 结合下图,说明UART的工作原理

    结合下图 xff0c 说明UART的工作原理 UART提供三个独立的异步串行I O口 xff0c 他们可以运行于中断模式或者DMA模式 xff0c 也就是说UART可以产生中断请求或者DMA请求 xff0c 以便在CPU和UART之间传输数
  • 深入理解计算机系统 -- 大端与小端字节序

    一 大端字节序 vs 小端字节序 字节序指一个多字节对象在内存中存储的方式 xff0c 小端字节序机器在存储多字节对象时采用低地址存低有效字节的策略 xff0c 大端则恰恰相反 字节序由CPU架构决定 xff0c 与操作系统无直接关系 像常
  • TCP连接建立

    TCP 一种面向来连接的 可靠的 基于字节流的传输层通信协议 面向连接 xff1a 数据在发送之前必须在两端建立连接 xff0c 方法就是我们熟知的三次握手连接 可靠传输 xff1a 通过多种机制来保证数据的正确传输 xff0c 比如序列号
  • UDP接收端收不到广播的消息问题排查

    网络调试助手可以互相发送 xff0c 而你的UDP广播代码却不行 你是广播 是不会被路由器转发的 但是在同一个交换机下 是可以收到广播的 还有就是 电脑的虚拟网卡会拦截广播操作 xff0c 因为你没有指定一个地址 xff0c 所以代码正确的
  • STM32带FIFO的DMA传输应用示例

    STM32系列芯片都内置DMA外设 xff0c 其中很多系列的DMA配备了FIFO 这里以STM32F429芯片及开发板为例 xff0c 演示一下带FIFO的DMA传输实现过程 大致情况是这样的 xff0c 我用TIMER1通道1的比较事件
  • 两种方式判断内存的大小端存储方式

    1 目的 xff1a 判断ubuntu操作系统的内存属于大端还是小端存储 2 源代码 两种方法判断大小端 xff08 处理器取值时的字节序 xff09 xff1a 1 字符指针 2 联合体 法1 if 1 include lt stdio
  • 字符串:求str1在str2中首次出现的位置。

    span class token macro property span class token directive keyword include span span class token string lt stdio h gt sp
  • 基于ROS利用客户端和服务端实现C++节点和python节点间传送图像

    基于ROS利用客户端和服务端实现C 43 43 节点和python节点间传送图像 配置ROS下和python3通信以及配置python3可用的cv bridge 环境安装和使用 参考 xff1a https blog csdn net qq
  • iMaxB6充电介绍

    iMaxB6是一款多用途充电器 xff0c 能够为Li ion Li Poly Li Fe Ni Cd Ni MH和Pb类型电池充电 xff0c 支持6串以内的平衡充电 简要步骤 xff1a 1 连接正负电源 xff1b 2 连接平衡线 x
  • 基于stm32串口环形缓冲队列处理机制

    原文链接 xff1a 基于stm32串口环形缓冲队列处理机制 入门级 xff08 单字节 xff09 串口环形缓冲区实验 1 1 实验简介 最简单的串口数据处理机制是数据接收并原样回发的机制是 xff1a 成功接收到一个数 xff0c 触发
  • 源码安装nginx 1.23.1

    先看看仓库们 yum list nginx 已加载插件 xff1a fastestmirror langpacks Loading mirror speeds from cached hostfile base mirrors aliyun
  • Sublime Text运行C和C++程序

    原文链接 xff1a Sublime Text运行C和C 43 43 程序 Sublime Text 是一款当下非常流行的文本编辑器 xff0c 其功能强大 xff08 提供有众多的插件 xff09 界面简洁 还支持跨平台使用 xff08
  • keilC51编译常见错误和警告说明

    如对编译出错感兴趣的网友能否把你们常遇到的错误信息收集起来并提出最终的解决办法加以归纳以期共享 xff01 1 L15 重复调用 WARNING L15 MULTIPLE CALL TO SEGMENT SEGMENT PR SPI REC
  • RS485的电路以及相关波形

    1 RS485的电路 xff0c 要注意RE引脚一般是和DE引脚接在一起的 2 差分信号AB的波形 xff0c 高电平6 2v左右 xff0c 低电平 3v 3 A点的波形 4 B点波形 5 接收RX的波形
  • Ubuntu安装cmake

    Ubuntu18 04安装cmake 转载自https www cnblogs com yanqingyang p 12731855 html 一 使用安装命令 span class token function sudo span apt
  • C/C++混淆点-strcat和strcpy区别

    一 原因分析 假设 xff1a char str 61 NULL str 61 new char 11 你想为字符串str开辟一个存储十个字符的内存空间 xff0c 然后你现在有两个字符串 xff1a char c1 61 34 abc 3

随机推荐