unity探索者之socket传输protobuf字节流(三)
版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/6986474.html
上一篇讲到了数据的处理,这一篇主要讲使用多线程收发消息
//创建消息数据模型
//正式项目中,消息的结构一般是消息长度+消息id+消息主体内容
public class Message
{
public IExtensible protobuf;
public int messageId;
} public class SocketClientTemp : MonoBehaviour
{
const int packageMaxLength = ; Socket mSocket;
Thread threadSend;
Thread threadRecive;
Queue<Message> allMessages = new Queue<Message>();
Queue<byte[]> sendQueue = new Queue<byte[]>(); public bool Init()
{
//创建一个socket对象
mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
return SocketConnection("此处是ip", );
} void Update()
{
AnalysisMessage();
} /// <summary>
/// 建立服务器连接
/// </summary>
/// <param name="ip">服务器的ip地址</param>
/// <param name="port">端口</param>
bool SocketConnection(string ip, int port)
{
try
{
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(ip), port);
//同步连接服务器,实际使用时推荐使用异步连接,处理方式会在下一篇讲断线重连时讲到
mSocket.Connect(ipep);
//连接成功后,创建两个线程,分别用于发送和接收消息
threadSend = new Thread(new ThreadStart(SendMessage));
threadSend.Start();
threadRecive = new Thread(new ThreadStart(ReceiveMessage));
threadRecive.Start();
return true;
}
catch (Exception e)
{
Debug.Log(e.ToString());
Close();
return false;
}
} #region ...发送消息
/// <summary>
/// 添加数据到发送队列
/// </summary>
/// <param name="protobufModel"></param>
/// <param name="messageId"></param>
public void AddSendMessageQueue(IExtensible protobufModel, int messageId)
{
sendQueue.Enqueue(BuildPackage(protobufModel, messageId));
} void SendMessage()
{
//循环获取发送队列中第一个数据,然后发送到服务器
while (true)
{
if (sendQueue.Count == )
{
Thread.Sleep();
continue;
}
if (!mSocket.Connected)
{
Close();
break;
}
else
Send(sendQueue.Peek());//发送队列中第一条数据
}
} void Send(byte[] bytes)
{
try
{
mSocket.Send(bytes, SocketFlags.None);
//发送成功后,从发送队列中移除已发送的消息
sendQueue.Dequeue();
}
catch (SocketException e)
{
//如果错误码为10035,说明服务器缓存区满了,所以等100毫秒再次发送
if (e.NativeErrorCode == )
{
Thread.Sleep();
Send(bytes);
}
else
Debug.Log(e.ToString());
}
}
#endregion #region ...接收消息
/// <summary>
/// 解析收到的消息
/// </summary>
void AnalysisMessage()
{
while (allMessages.Count > )
{
int id = allMessages.Dequeue().messageId;
switch (id)
{
//根据消息id做不同的处理
}
}
} /// <summary>
/// 接收数据
/// </summary>
void ReceiveMessage()
{
while (true)
{
if (!mSocket.Connected)
break;
byte[] recvBytesHead = GetBytesReceive();
int bodyLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(recvBytesHead, ));
byte[] recvBytesBody = GetBytesReceive(bodyLength); byte[] messageId = new byte[];
Array.Copy(recvBytesBody, , messageId, , );
byte[] messageBody = new byte[bodyLength - ];
Array.Copy(recvBytesBody, , messageBody, , bodyLength - ); if (BitConverter.IsLittleEndian)
Array.Reverse(messageId);
FillAllPackages(BitConverter.ToInt32(messageId, ), messageBody);
}
} /// <summary>
/// 填充接收消息队列
/// </summary>
/// <param name="messageId"></param>
/// <param name="messageBody"></param>
void FillAllPackages(int messageId, byte[] messageBody)
{
switch (messageId)
{
//根据消息id处理消息,并添加到接收消息队列
case :
allMessages.Enqueue(new Message()
{
protobuf = ProtobufSerilizer.DeSerialize<TestTemp>(messageBody),
messageId = messageId
});
break;
}
} /// <summary>
/// 接收数据并处理
/// </summary>
/// <param name="length"></param>
/// <returns></returns>
byte[] GetBytesReceive(int length)
{
byte[] recvBytes = new byte[length];
while (length > )
{
byte[] receiveBytes = new byte[length < packageMaxLength ? length : packageMaxLength];
int iBytesBody = ;
if (length >= receiveBytes.Length)
iBytesBody = mSocket.Receive(receiveBytes, receiveBytes.Length, );
else
iBytesBody = mSocket.Receive(receiveBytes, length, );
receiveBytes.CopyTo(recvBytes, recvBytes.Length - length);
length -= iBytesBody;
}
return recvBytes;
}
#endregion /// <summary>
/// 构建消息数据包
/// </summary>
/// <param name="protobufModel"></param>
/// <param name="messageId"></param>
byte[] BuildPackage(IExtensible protobufModel, int messageId)
{
byte[] b;
if (protobufModel != null)
b = ProtobufSerilizer.Serialize(protobufModel);
else
b = new byte[];
//消息长度(int数据,长度4) + 消息id(int数据,长度4) + 消息主体内容
ByteBuffer buf = ByteBuffer.Allocate(b.Length + + );
//消息长度 = 消息主体内容长度 + 消息id长度
buf.WriteInt(b.Length + );
buf.WriteInt(messageId); if (protobufModel != null)
buf.WriteBytes(b);
return buf.GetBytes();
} void OnDestroy()
{
//停止运行后,如果不关闭socket多线程,再次运行时,unity会卡死
Close();
} /// <summary>
/// 关闭socket,终止线程
/// </summary>
public void Close()
{
if (mSocket != null)
{
//微软官方推荐在关闭socket前先shutdown,但是经过测试,发现网络断开后,shutdown会无法执行
if (mSocket.Connected)
mSocket.Shutdown(SocketShutdown.Both);
mSocket.Close();
mSocket = null;
}
//关闭线程
if (threadSend != null)
threadSend.Abort();
if (threadRecive != null)
threadRecive.Abort();
threadSend = null;
threadRecive = null;
}
}
到这里,使用socket处理消息的收发就基本结束了,但是,某些项目为了增强体验,可能还会增加断线重连的功能,这个功能会在下一篇讲到
unity探索者之socket传输protobuf字节流(三)的更多相关文章
- unity探索者之socket传输protobuf字节流(一)
版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/6974229.html 近期在做一个棋牌项目,需要用到socket传输protobu ...
- unity探索者之socket传输protobuf字节流(二)
版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/6977935.html 上一篇主要说的是protobuf字节流的序列化和解析,将pr ...
- unity探索者之socket传输protobuf字节流(四)
版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/7027659.html 上篇已经把socket的传输说的差不多了,这篇主要是说说断线 ...
- C#使用ProtocolBuffer(ProtoBuf)进行Unity中的Socket通信
首先来说一下本文中例子所要实现的功能: 基于ProtoBuf序列化对象 使用Socket实现时时通信 数据包的编码和解码 下面来看具体的步骤: 一.Unity中使用ProtoBuf 导入DLL到Uni ...
- Unity C# 自定义TCP传输协议以及封包拆包、解决粘包问题
本文只是初步实现了一个简单的TCP自定协议,更为复杂的协议可以根据这种方式去扩展. TCP协议,通俗一点的讲,它是一种基于socket传输的由发送方和接收方事先协商好的一种消息包组成结构,主要由消息头 ...
- Java 学习笔记 网络编程 使用Socket传输文件 CS模式
Socket的简单认识 Socket是一种面向连接的通信协议,Socket应用程序是一种C/S(Client端/Server端)结构的应用程序 Socket是两台机器间通信的端点. Socket是连接 ...
- C++ socket 传输不同类型数据的四种方式
使用socket传输组织好的不同类型数据,有四种不同的方式(我知道的嘿嘿): a. 结构体 b. Json序列化 c. 类对象 d. protobuf 下面逐一整理一下,方便以后进行项目开发. 1. ...
- 【Unity Shaders】学习笔记——SurfaceShader(三)BasicDiffuse和HalfLambert
[Unity Shaders]学习笔记——SurfaceShader(三)BasicDiffuse和HalfLambert 转载请注明出处:http://www.cnblogs.com/-867259 ...
- [C#技术参考]Socket传输结构数据
最近在做一个机器人项目,要实时的接收机器人传回的坐标信息,并在客户端显示当前的地图和机器人的位置.当然坐标的回传是用的Socket,用的是C++的结构体表示的坐标信息.但是C#不能像C++那样很eas ...
随机推荐
- consul++ansible+shell批量下发注册node_exporter
--日期:2020年7月21日 --作者:飞翔的小胖猪 文档功能说明: 文档通过ansible+shell+consul的方式实现批量下发安装Linux操作系统监控的node_exporter软件, ...
- ActiveMQ【CVE-2016-3088】上传公钥实现sssh免密登录
Apache-ActiveMQ是apache旗下的消息中间件,至今为止还是有较多的甲方爸爸们,还在使用该中间件.据了解,Apache-ActiveMQ中间件有2个厉害的CVE,一个是CVE-2016- ...
- Markdown显示测试
这是一个一级标题 文本1 文本2 这是一个二级标题 斜体 粗体 粗斜体 下面是分割线 上面是分割线 删除线 下划线 脚注[1] 这是一个三级标题 无序列表1 内容 无序列表2 内容 无序列表3 有序列 ...
- 第四课 OOP封装继承多态解析,接口抽象类选择 2019-04-21
父类 xx = new 子类(); xx.method(); 1 普通方法由编译时决定(左边) --- 提高效率 2 虚方法(virtual) 由运行时决定-- -多态,灵活 3 抽象方法由运行时决 ...
- pandas处理excel文件和csv文件
一.csv文件 csv以纯文本形式存储表格数据 pd.read_csv('文件名'),可添加参数engine='python',encoding='gbk' 一般来说,windows系统的默认编码为g ...
- link小图标以及表格的用法基础
一.网页小图标的实现 实例: 实现方式: 效果: 二.表格基础 1.表格的组合标签 常用: table tr td caption ①table属性 border 边框 width 宽度 默认按照 ...
- Myeclipse-10.7.1版本破解
自从上次写了IDEA2020版本破解方式,这次写一下Myeclipse10.7.1版本破解 下方链接是IDEA破解教程 点击即可跳转 Myeclipse下载地址这里我上传到了百度网盘 这里提取码 ...
- Day03_企业权限管理(SSM整合)
学于黑马程序员和传智播客联合做的教学项目 感谢 黑马程序员官网 传智播客官网 个人根据教程的每天的工作进度的代码和资料 密码:cti5 b站在线视频 微信搜索"艺术行者",关注并回 ...
- 了不起的Node.js 5/16
Chapter 1 安装 1.Node.js的设计理念之一,只维护较少量的依赖,这使得安装node.js变得非常简单. 2.执行文件console.log没问题,但是node执行http静态服务器的时 ...
- Vue数据产生变化需要页面渲染完之后执行某操作
1.数据产生变化或者页面需要vue数据渲染完之后加载的东西 Vue.nextTick(function () { alert(123); }); 2 调用vue方法 --------------Vue ...