概述
制定通信协议
一 什么是制定通信协议?
客户端在和服务器进行通信的时候,为了让双方都能辨别接收到的消息的内容,由发送方和接收方而制定的相关约定。
二 为什么制定通信协议?
在大型的网络游戏中,客户端和服务器发送的消息会很多很多,我们为了方便管理和区分这些消息,所以需要制定一些协议。比如说游戏登陆的时候,需要向服务器发送登陆的信息,我们把信息封装在登陆协议里面,直接把登陆协议发送给服务器。在做同步的时候,需要向服务器发送位置的信息,直接把同步协议发送给服务器,服务器可以通过协议的编号,来判断收到的协议是哪种协议,从而获取到相关的信息。
三 通信协议一般在哪里会用到?
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;
}
最后
以上就是瘦瘦机器猫为你收集整理的制定通信协议的全部内容,希望文章能够帮你解决制定通信协议所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复