版权声明:本文为原创文章,转载请声明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. 动态DP,ddp

    动态DP?动态动态规划? 个人理解:动态DP,就是普通DP加修改操作,然后就变成了个毒瘤题. 直接就着例题写吧. 例题 P4719 [模板]"动态 DP"&动态树分治 求树 ...

  2. INS(Instagram)如何绑定谷歌二次验证码/谷歌身份验证/双重认证?

    1.打开Ins,找到双重验证界面   打开Ins,点击右上角“三”-“设置”-“安全”-“双重验证”-“选择安全验证方式”-“身份验证应用”-“立即开启”-“手动设置”-“复制密钥”-“输入验证码” ...

  3. mybatis sqlsession与sqlsquery、transaction、connection

    sqlsession和connection 一个sqlsession一般对应一个connection,并且mybatis默认每次获取session都会开启一个事务,且不自动提交事务.如果更新操作完成后 ...

  4. VS Code小白使用教程

    本文来自作者:你不知道的巨蟹 原文链接 https://www.cnblogs.com/tu-0718/p/10935910.html,如有侵权,则可删除. 前言 现在使用Vscode编码的人越来越多 ...

  5. WEB简单的登录注册功能(分层)

    登录: 前端页面: <body> <form action="/webtext/LogingServlet" method="post"> ...

  6. Apple产品价钱分析

  7. PHP preg_grep() 函数

    preg_grep 函数用于返回匹配模式的数组条目.高佣联盟 www.cgewang.com 语法 array preg_grep ( string $pattern , array $input [ ...

  8. 5.19 省选模拟赛 小B的夏令营 概率 dp 前缀和优化dp

    LINK:小B的夏令营 这道题是以前从没见过的优化dp的方法 不过也在情理之中. 注意读题 千万不要像我这个sb一样 考完连题意都不知道是啥. 一个长方形 要求从上到下联通的概率. 容易发现 K天只是 ...

  9. 剑指 Offer 58 - I. 翻转单词顺序

    本题 题目链接 题目描述 我的题解 方法一:库函数split() 要注意str.split()函数: 字符串str前有 n 个空格时,分割出来的字符串列表中会多出 n 个空字符串: 字符串str某两个 ...

  10. Oracle连接池工具类OJDBCUtils

    Oraclejdbc.properties driverClassName=oracle.jdbc.driver.OracleDriver url=jdbc:oracle:thin:@127.0.0. ...