版权声明:本文为原创文章,转载请声明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字节流(三)的更多相关文章

  1. unity探索者之socket传输protobuf字节流(一)

    版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/6974229.html 近期在做一个棋牌项目,需要用到socket传输protobu ...

  2. unity探索者之socket传输protobuf字节流(二)

    版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/6977935.html 上一篇主要说的是protobuf字节流的序列化和解析,将pr ...

  3. unity探索者之socket传输protobuf字节流(四)

    版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/7027659.html 上篇已经把socket的传输说的差不多了,这篇主要是说说断线 ...

  4. C#使用ProtocolBuffer(ProtoBuf)进行Unity中的Socket通信

    首先来说一下本文中例子所要实现的功能: 基于ProtoBuf序列化对象 使用Socket实现时时通信 数据包的编码和解码 下面来看具体的步骤: 一.Unity中使用ProtoBuf 导入DLL到Uni ...

  5. Unity C# 自定义TCP传输协议以及封包拆包、解决粘包问题

    本文只是初步实现了一个简单的TCP自定协议,更为复杂的协议可以根据这种方式去扩展. TCP协议,通俗一点的讲,它是一种基于socket传输的由发送方和接收方事先协商好的一种消息包组成结构,主要由消息头 ...

  6. Java 学习笔记 网络编程 使用Socket传输文件 CS模式

    Socket的简单认识 Socket是一种面向连接的通信协议,Socket应用程序是一种C/S(Client端/Server端)结构的应用程序 Socket是两台机器间通信的端点. Socket是连接 ...

  7. C++ socket 传输不同类型数据的四种方式

    使用socket传输组织好的不同类型数据,有四种不同的方式(我知道的嘿嘿): a. 结构体 b. Json序列化 c. 类对象 d. protobuf 下面逐一整理一下,方便以后进行项目开发. 1. ...

  8. 【Unity Shaders】学习笔记——SurfaceShader(三)BasicDiffuse和HalfLambert

    [Unity Shaders]学习笔记——SurfaceShader(三)BasicDiffuse和HalfLambert 转载请注明出处:http://www.cnblogs.com/-867259 ...

  9. [C#技术参考]Socket传输结构数据

    最近在做一个机器人项目,要实时的接收机器人传回的坐标信息,并在客户端显示当前的地图和机器人的位置.当然坐标的回传是用的Socket,用的是C++的结构体表示的坐标信息.但是C#不能像C++那样很eas ...

随机推荐

  1. web自动化 -- HTMLreport(一)测试报告自定义测试用例名,重写ddt

    一.需求痛点 1.HTMLreport测试报告的用例名不明确 2.希望可以自定义HTMLreport测试报告的用例名 3.痛点截图 二.解决办法 1.原因分析 HTMLreport测试报告中的用例名是 ...

  2. 前端学习(十六):JavaScript运算

    进击のpython ***** 前端学习--JavaScript运算 在这一节之前,应该做到的是对上一节的数据类型的相关方法都敲一遍,加深印象 这部分的知识的特点就是碎而且杂,所以一定要多练~练习起来 ...

  3. 【Laravel 】数据迁移文件常用方法速查表

    一.存储引擎.字符编码相关操作 命令 描述 $table->engine = 'InnoDB'; 指定表的存储引擎(MySQL) $table->charset = 'utf8'; 指定数 ...

  4. IO—》字节流&字符流

    字节流 一.字节输出流OutputStream OutputStream此抽象类,是表示输出字节流的所有类的超类.操作的数据都是字节,定义了输出字节流的基本共性功能方法. FileOutputStre ...

  5. 数字转字符串&&字符串转数字

    一开始写错了呜呜呜 先是<< 再是>>

  6. Explain关键字解析

    Explain 用法 explain模拟Mysql优化器是如何执行SQL查询语句的,从而知道Mysql是如何处理你的SQL语句的.分析你的查询语句或是表结构的性能瓶颈. 语法:Explain + SQ ...

  7. 接口工具Apifox

    最近发现一款接口测试工具--apifox,我我们很难将它描述为一款接口管理工具 或 接口自测试工具. 官方给了一个简单的公式,更能说明apifox可以做什么. Apifox = Postman + S ...

  8. PHP curl_share_setopt函数

    (PHP 5 >= 5.5.0) curl_share_setopt — 设置 cURL 共享句柄的一个选项. 说明 bool curl_share_setopt ( resource $sh ...

  9. PHP fstat() 函数

    定义和用法 fstat() 函数返回关于一个打开的文件的信息. 该函数将返回一个包含下列元素的数组: [0] 或 [dev] - 设备编号 [1] 或 [ino] - inode 编号 [2] 或 [ ...

  10. PHP preg_replace() 函数

    preg_replace 函数执行一个正则表达式的搜索和替换.高佣联盟 www.cgewang.com 语法 mixed preg_replace ( mixed $pattern , mixed $ ...