版权声明:本文为原创文章,转载请声明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. SW算法求全局最小割(Stoer-Wagner算法)

    我找到的唯一能看懂的题解:[ZZ]最小割集Stoer-Wagner算法 似乎是一个冷门算法,连oi-wiki上都没有,不过洛谷上竟然有它的模板题,并且2017百度之星的资格赛还考到了.于是来学习一下. ...

  2. C# POST请求中raw 参数的传递

    public static string PostmanPost() { var client = new RestClient("http://119.3.248.64:3000" ...

  3. PHP设计模式之----简单工厂模式

    定义个抽象的类(或接口),让子类去继承(实现)它 abstract class Operation { abstract public function getValue($num1, $num2); ...

  4. jQuery与javascript

    jQuery 是一个 JavaScript 库,jQuery 极大地简化了 JavaScript 编程. javaScript(js)和jQuery(jq) 都是找元素.操作元素 Dom操作的区别: ...

  5. 做完这套面试题,你才敢说懂Excel

    下面的题目来自一份商品专员的面试题,其中有涉及到条件格式.自定义排序.数据验证制作下拉菜单.查找引用类函数.文本提取函数等等技能. 满满的干货技能可不是商品专员“专属”,如果你能熟练掌握,在平日工作中 ...

  6. ES6标准入门 2/26

    第一章 ECMAScript6 简介 1.首先经典开头,ECMAScript跟JavaScript的关系,前者是后者的规格,后者是前者的一种实现.在日常场合中,这两个词是可以互换的. 2.ES6可以泛 ...

  7. Python os.readlink() 方法

    概述 os.readlink() 方法用于返回软链接所指向的文件.可能返回绝对或相对路径.高佣联盟 www.cgewang.com 在Unix中有效 语法 readlink()方法语法格式如下: os ...

  8. PHP array_reverse() 函数

    实例 返回翻转顺序的数组: <?php $a=array("a"=>"Volvo","b"=>"BMW" ...

  9. 5.15 省选模拟赛 容斥 生成函数 dp

    LINK:5.15 T2 个人感觉生成函数更无脑 容斥也好推的样子. 容易想到每次放数和数字的集合无关 所以得到一个dp f[i][j]表示前i个数字 逆序对为j的方案数. 容易得到转移 使用前缀和优 ...

  10. 4.深入k8s:容器持久化存储

    从一个例子入手PV.PVC Kubernetes 项目引入了一组叫作 Persistent Volume Claim(PVC)和 Persistent Volume(PV)的 API 对象用于管理存储 ...