【澳门葡京备用网址】unity查究者之socket传输protobuf字节流,学习Unet的一些历程

版权表明:正文为原创文章,转载请宣示 

unity探求者之socket传输protobuf字节流(三),unityprotobuf

版权申明:正文为原创小说,转发请宣示 

上一篇讲到了多少的处理,这一篇首要讲使用十二线程收发音讯

  1 //创建消息数据模型
  2 //正式项目中,消息的结构一般是消息长度+消息id+消息主体内容
  3 public class Message
  4 {
  5     public IExtensible protobuf;
  6     public int messageId;
  7 }
  8 
  9 public class SocketClientTemp : MonoBehaviour
 10 {
 11     const int packageMaxLength = 1024;
 12 
 13     Socket mSocket;
 14     Thread threadSend;
 15     Thread threadRecive;
 16     Queue<Message> allMessages = new Queue<Message>();
 17     Queue<byte[]> sendQueue = new Queue<byte[]>();
 18 
 19     public bool Init()
 20     {
 21         //创建一个socket对象
 22         mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 23         return SocketConnection("此处是ip", 1111);
 24     }
 25 
 26     void Update()
 27     {
 28         AnalysisMessage();
 29     }
 30 
 31     /// <summary>
 32     /// 建立服务器连接
 33     /// </summary>
 34     /// <param name="ip">服务器的ip地址</param>
 35     /// <param name="port">端口</param>
 36     bool SocketConnection(string ip, int port)
 37     {
 38         try
 39         {
 40             IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(ip), port);
 41             //同步连接服务器,实际使用时推荐使用异步连接,处理方式会在下一篇讲断线重连时讲到
 42             mSocket.Connect(ipep);
 43             //连接成功后,创建两个线程,分别用于发送和接收消息
 44             threadSend = new Thread(new ThreadStart(SendMessage));
 45             threadSend.Start();
 46             threadRecive = new Thread(new ThreadStart(ReceiveMessage));
 47             threadRecive.Start();
 48             return true;
 49         }
 50         catch (Exception e)
 51         {
 52             Debug.Log(e.ToString());
 53             Close();
 54             return false;
 55         }
 56     }
 57 
 58     #region ...发送消息
 59     /// <summary>
 60     /// 添加数据到发送队列
 61     /// </summary>
 62     /// <param name="protobufModel"></param>
 63     /// <param name="messageId"></param>
 64     public void AddSendMessageQueue(IExtensible protobufModel, int messageId)
 65     {
 66         sendQueue.Enqueue(BuildPackage(protobufModel, messageId));
 67     }
 68 
 69     void SendMessage()
 70     {
 71         //循环获取发送队列中第一个数据,然后发送到服务器
 72         while (true)
 73         {
 74             if (sendQueue.Count == 0)
 75             {
 76                 Thread.Sleep(100);
 77                 continue;
 78             }
 79             if (!mSocket.Connected)
 80             {
 81                 Close();
 82                 break;
 83             }
 84             else
 85                 Send(sendQueue.Peek());//发送队列中第一条数据
 86         }
 87     }
 88 
 89     void Send(byte[] bytes)
 90     {
 91         try
 92         {
 93             mSocket.Send(bytes, SocketFlags.None);
 94             //发送成功后,从发送队列中移除已发送的消息
 95             sendQueue.Dequeue();
 96         }
 97         catch (SocketException e)
 98         {
 99             //如果错误码为10035,说明服务器缓存区满了,所以等100毫秒再次发送
100             if (e.NativeErrorCode == 10035)
101             {
102                 Thread.Sleep(100);
103                 Send(bytes);
104             }
105             else
106                 Debug.Log(e.ToString());
107         }
108     }
109     #endregion
110 
111     #region ...接收消息
112     /// <summary>
113     /// 解析收到的消息
114     /// </summary>
115     void AnalysisMessage()
116     {
117         while (allMessages.Count > 0)
118         {
119             int id = allMessages.Dequeue().messageId;
120             switch (id)
121             {
122                 //根据消息id做不同的处理
123             }
124         }
125     }
126 
127     /// <summary>
128     /// 接收数据
129     /// </summary>
130     void ReceiveMessage()
131     {
132         while (true)
133         {
134             if (!mSocket.Connected)
135                 break;
136             byte[] recvBytesHead = GetBytesReceive(4);
137             int bodyLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(recvBytesHead, 0));
138             byte[] recvBytesBody = GetBytesReceive(bodyLength);
139 
140             byte[] messageId = new byte[4];
141             Array.Copy(recvBytesBody, 0, messageId, 0, 4);
142             byte[] messageBody = new byte[bodyLength - 4];
143             Array.Copy(recvBytesBody, 4, messageBody, 0, bodyLength - 4);
144 
145             if (BitConverter.IsLittleEndian)
146                 Array.Reverse(messageId);
147             FillAllPackages(BitConverter.ToInt32(messageId, 0), messageBody);
148         }
149     }
150 
151     /// <summary>
152     /// 填充接收消息队列
153     /// </summary>
154     /// <param name="messageId"></param>
155     /// <param name="messageBody"></param>
156     void FillAllPackages(int messageId, byte[] messageBody)
157     {
158         switch (messageId)
159         {
160             //根据消息id处理消息,并添加到接收消息队列
161             case 1:
162                 allMessages.Enqueue(new Message() 
163                 {
164                     protobuf = ProtobufSerilizer.DeSerialize<TestTemp>(messageBody), 
165                     messageId = messageId 
166                 });
167                 break;
168         }
169     }
170 
171     /// <summary>
172     /// 接收数据并处理
173     /// </summary>
174     /// <param name="length"></param>
175     /// <returns></returns>
176     byte[] GetBytesReceive(int length)
177     {
178         byte[] recvBytes = new byte[length];
179         while (length > 0)
180         {
181             byte[] receiveBytes = new byte[length < packageMaxLength ? length : packageMaxLength];
182             int iBytesBody = 0;
183             if (length >= receiveBytes.Length)
184                 iBytesBody = mSocket.Receive(receiveBytes, receiveBytes.Length, 0);
185             else
186                 iBytesBody = mSocket.Receive(receiveBytes, length, 0);
187             receiveBytes.CopyTo(recvBytes, recvBytes.Length - length);
188             length -= iBytesBody;
189         }
190         return recvBytes;
191     }
192     #endregion
193 
194     /// <summary>
195     /// 构建消息数据包
196     /// </summary>
197     /// <param name="protobufModel"></param>
198     /// <param name="messageId"></param>
199     byte[] BuildPackage(IExtensible protobufModel, int messageId)
200     {
201         byte[] b;
202         if (protobufModel != null)
203             b = ProtobufSerilizer.Serialize(protobufModel);
204         else
205             b = new byte[0];
206         //消息长度(int数据,长度4) + 消息id(int数据,长度4) + 消息主体内容
207         ByteBuffer buf = ByteBuffer.Allocate(b.Length + 4 + 4);
208         //消息长度 = 消息主体内容长度 + 消息id长度
209         buf.WriteInt(b.Length + 4);
210         buf.WriteInt(messageId);
211 
212         if (protobufModel != null)
213             buf.WriteBytes(b);
214         return buf.GetBytes();
215     }
216 
217     void OnDestroy()
218     {
219         //停止运行后,如果不关闭socket多线程,再次运行时,unity会卡死
220         Close();
221     }
222 
223     /// <summary>
224     /// 关闭socket,终止线程
225     /// </summary>
226     public void Close()
227     {
228         if (mSocket != null)
229         {
230             //微软官方推荐在关闭socket前先shutdown,但是经过测试,发现网络断开后,shutdown会无法执行
231             if (mSocket.Connected)
232                 mSocket.Shutdown(SocketShutdown.Both);
233             mSocket.Close();
234             mSocket = null;
235         }
236         //关闭线程
237         if (threadSend != null)
238             threadSend.Abort();
239         if (threadRecive != null)
240             threadRecive.Abort();
241         threadSend = null;
242         threadRecive = null;
243     }
244 }

到那边,使用socket管理音信的收发就大旨甘休了,可是,有个别品种为了增进体验,恐怕还会增多断线重连的职能,那一个功用会在下1篇讲到

【澳门葡京备用网址】unity查究者之socket传输protobuf字节流,学习Unet的一些历程。
版权评释:
本文为原创文章,转载请宣示
上…

 

上一篇讲到了数额的管理,那一篇首要讲使用八线程收发音信

结束学业设计的体系,须要用到手提式有线电话机作为调节端,那就须要用到Unity的网络模块。

  1 //创建消息数据模型
  2 //正式项目中,消息的结构一般是消息长度+消息id+消息主体内容
  3 public class Message
  4 {
  5     public IExtensible protobuf;
  6     public int messageId;
  7 }
  8 
  9 public class SocketClientTemp : MonoBehaviour
 10 {
 11     const int packageMaxLength = 1024;
 12 
 13     Socket mSocket;
 14     Thread threadSend;
 15     Thread threadRecive;
 16     Queue<Message> allMessages = new Queue<Message>();
 17     Queue<byte[]> sendQueue = new Queue<byte[]>();
 18 
 19     public bool Init()
 20     {
 21         //创建一个socket对象
 22         mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 23         return SocketConnection("此处是ip", 1111);
 24     }
 25 
 26     void Update()
 27     {
 28         AnalysisMessage();
 29     }
 30 
 31     /// <summary>
 32     /// 建立服务器连接
 33     /// </summary>
 34     /// <param name="ip">服务器的ip地址</param>
 35     /// <param name="port">端口</param>
 36     bool SocketConnection(string ip, int port)
 37     {
 38         try
 39         {
 40             IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(ip), port);
 41             //同步连接服务器,实际使用时推荐使用异步连接,处理方式会在下一篇讲断线重连时讲到
 42             mSocket.Connect(ipep);
 43             //连接成功后,创建两个线程,分别用于发送和接收消息
 44             threadSend = new Thread(new ThreadStart(SendMessage));
 45             threadSend.Start();
 46             threadRecive = new Thread(new ThreadStart(ReceiveMessage));
 47             threadRecive.Start();
 48             return true;
 49         }
 50         catch (Exception e)
 51         {
 52             Debug.Log(e.ToString());
 53             Close();
 54             return false;
 55         }
 56     }
 57 
 58     #region ...发送消息
 59     /// <summary>
 60     /// 添加数据到发送队列
 61     /// </summary>
 62     /// <param name="protobufModel"></param>
 63     /// <param name="messageId"></param>
 64     public void AddSendMessageQueue(IExtensible protobufModel, int messageId)
 65     {
 66         sendQueue.Enqueue(BuildPackage(protobufModel, messageId));
 67     }
 68 
 69     void SendMessage()
 70     {
 71         //循环获取发送队列中第一个数据,然后发送到服务器
 72         while (true)
 73         {
 74             if (sendQueue.Count == 0)
 75             {
 76                 Thread.Sleep(100);
 77                 continue;
 78             }
 79             if (!mSocket.Connected)
 80             {
 81                 Close();
 82                 break;
 83             }
 84             else
 85                 Send(sendQueue.Peek());//发送队列中第一条数据
 86         }
 87     }
 88 
 89     void Send(byte[] bytes)
 90     {
 91         try
 92         {
 93             mSocket.Send(bytes, SocketFlags.None);
 94             //发送成功后,从发送队列中移除已发送的消息
 95             sendQueue.Dequeue();
 96         }
 97         catch (SocketException e)
 98         {
 99             //如果错误码为10035,说明服务器缓存区满了,所以等100毫秒再次发送
100             if (e.NativeErrorCode == 10035)
101             {
102                 Thread.Sleep(100);
103                 Send(bytes);
104             }
105             else
106                 Debug.Log(e.ToString());
107         }
108     }
109     #endregion
110 
111     #region ...接收消息
112     /// <summary>
113     /// 解析收到的消息
114     /// </summary>
115     void AnalysisMessage()
116     {
117         while (allMessages.Count > 0)
118         {
119             int id = allMessages.Dequeue().messageId;
120             switch (id)
121             {
122                 //根据消息id做不同的处理
123             }
124         }
125     }
126 
127     /// <summary>
128     /// 接收数据
129     /// </summary>
130     void ReceiveMessage()
131     {
132         while (true)
133         {
134             if (!mSocket.Connected)
135                 break;
136             byte[] recvBytesHead = GetBytesReceive(4);
137             int bodyLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(recvBytesHead, 0));
138             byte[] recvBytesBody = GetBytesReceive(bodyLength);
139 
140             byte[] messageId = new byte[4];
141             Array.Copy(recvBytesBody, 0, messageId, 0, 4);
142             byte[] messageBody = new byte[bodyLength - 4];
143             Array.Copy(recvBytesBody, 4, messageBody, 0, bodyLength - 4);
144 
145             if (BitConverter.IsLittleEndian)
146                 Array.Reverse(messageId);
147             FillAllPackages(BitConverter.ToInt32(messageId, 0), messageBody);
148         }
149     }
150 
151     /// <summary>
152     /// 填充接收消息队列
153     /// </summary>
154     /// <param name="messageId"></param>
155     /// <param name="messageBody"></param>
156     void FillAllPackages(int messageId, byte[] messageBody)
157     {
158         switch (messageId)
159         {
160             //根据消息id处理消息,并添加到接收消息队列
161             case 1:
162                 allMessages.Enqueue(new Message() 
163                 {
164                     protobuf = ProtobufSerilizer.DeSerialize<TestTemp>(messageBody), 
165                     messageId = messageId 
166                 });
167                 break;
168         }
169     }
170 
171     /// <summary>
172     /// 接收数据并处理
173     /// </summary>
174     /// <param name="length"></param>
175     /// <returns></returns>
176     byte[] GetBytesReceive(int length)
177     {
178         byte[] recvBytes = new byte[length];
179         while (length > 0)
180         {
181             byte[] receiveBytes = new byte[length < packageMaxLength ? length : packageMaxLength];
182             int iBytesBody = 0;
183             if (length >= receiveBytes.Length)
184                 iBytesBody = mSocket.Receive(receiveBytes, receiveBytes.Length, 0);
185             else
186                 iBytesBody = mSocket.Receive(receiveBytes, length, 0);
187             receiveBytes.CopyTo(recvBytes, recvBytes.Length - length);
188             length -= iBytesBody;
189         }
190         return recvBytes;
191     }
192     #endregion
193 
194     /// <summary>
195     /// 构建消息数据包
196     /// </summary>
197     /// <param name="protobufModel"></param>
198     /// <param name="messageId"></param>
199     byte[] BuildPackage(IExtensible protobufModel, int messageId)
200     {
201         byte[] b;
202         if (protobufModel != null)
203             b = ProtobufSerilizer.Serialize(protobufModel);
204         else
205             b = new byte[0];
206         //消息长度(int数据,长度4) + 消息id(int数据,长度4) + 消息主体内容
207         ByteBuffer buf = ByteBuffer.Allocate(b.Length + 4 + 4);
208         //消息长度 = 消息主体内容长度 + 消息id长度
209         buf.WriteInt(b.Length + 4);
210         buf.WriteInt(messageId);
211 
212         if (protobufModel != null)
213             buf.WriteBytes(b);
214         return buf.GetBytes();
215     }
216 
217     void OnDestroy()
218     {
219         //停止运行后,如果不关闭socket多线程,再次运行时,unity会卡死
220         Close();
221     }
222 
223     /// <summary>
224     /// 关闭socket,终止线程
225     /// </summary>
226     public void Close()
227     {
228         if (mSocket != null)
229         {
230             //微软官方推荐在关闭socket前先shutdown,但是经过测试,发现网络断开后,shutdown会无法执行
231             if (mSocket.Connected)
232                 mSocket.Shutdown(SocketShutdown.Both);
233             mSocket.Close();
234             mSocket = null;
235         }
236         //关闭线程
237         if (threadSend != null)
238             threadSend.Abort();
239         if (threadRecive != null)
240             threadRecive.Abort();
241         threadSend = null;
242         threadRecive = null;
243     }
244 }

因为只会计统计筹到多少个大致的开关命令,所以不打算做多么复杂的功能,1开头希图用C#的Socket编制程序,但是思虑到10贰线程的关联以为以后跟UI结合起来管理会比较麻烦,机智的本人说了算利用Unity提供的Unet互连网模块。无需太难为,只须求cs之间发送新闻就行。

到那边,使用socket管理音信的收发就着力结束了,但是,有些品种为了增加体验,可能还会加多断线重连的职能,这几个效应会在下一篇讲到

        
一齐头自己认为温馨掉进了3个坑,英特网关于Unet的例证不要太少,而且不少都以依照六人游玩的。最无语的少数正是个中最牛的Commands和ClientXC90pc的长途调用命令以及各样变量同步只援助同1对象的两样实例间调用,不难点来讲,正是客户端和服务器必须是同二个安装包,他俩只是打开药方式不等同。可是我只是想做一个调控器作为客户端而已呀。不过本人可能坚信,如此牛x的网络模块,断定会支撑笔者想要的这种傻x通信的。

        
小编仔细看了Unity全部的关于Network的零部件。最后将指标锁定在了NetworkManager上。接下来就是去看NetworkManager的API啦,果然,壹拉下来本人就通晓没错了

 澳门葡京备用网址 1

 

在Public
Functions里设有不少On起先的措施,对Unity稍微熟识一点的都了然以On初步的是Unity提供的回调函数,用于处监护人件。同时也找到了StartClient、Host、Server这一个主意。找到了常用的主宰方法和回调函数,接下去就是要找发送和吸收接纳音信的目的了。但是本身平昔不丝毫线索。小编说了算先将客户端和服务器连接成功再说。于是,小编先引用到NetworkManager的目的,在手提式有线电话机端调用StartClient方法,在计算机端调用StartSever方法。开采相互能再而三成功。

         接下来正是拍卖部分接连事件了,在Scripting
API文书档案里看NetworkManager类的Description。这里用1段简洁的代码清楚的介绍了什么运用那一个连接事件

 澳门葡京备用网址 2

 

澳门葡京备用网址 ,毋庸置疑,只必要写三个类承接NetworkManager类。就可以很轻松的重写回调函数,自定义连接事件了。何止爽歪歪。

        
然后我将原来增加的NetworkManager组件删掉,新建1个类并承接自NetworkManager。重写了一些内需接纳的回调方法。完美化解。

        
以后能接2连十分三功,并且也能管理部分连连事件了,接下去正是cs之间收发音讯了。依照经验,cs之间收发信息应该是依附某种对象调用Send方法和Receive方法。继续翻看API发现有诸如此类八个或者会用到的类NetworkServer、NetworkClient、NetworkReader、NetworkWriter、NetworkConnection。因为自己在NetworkServer、NetworkClient、NetworkConnection都找到了与send有关的点子;而NetworkReader、NetworkWriter1看就清楚是一个读3个写。不过却不曾在NetworkServer、NetworkClient、NetworkConnection那两个类找到任何与Receive有关的措施,就认为很意外。按理说有了NetworkReader、NetworkWriter就可以兑现自身想要的效能了,但是笔者意识回调函数都会传递一个NetworkConnection对象,所以作者可能愿意一向运用NetworkConnection类,于是放任了Reader和Writer,把关键放在了NetworkConnection类。继续看API…
仔细查阅了NetworkConnection的所以措施的介绍,开采有这么3个办法public
void RegisterHandler(short msgType, Networking.NetworkMessageDelegate
handler)
才意识到原来这里运用的是观望者方式。再进NetworkMessageDelegate看看这几个委托的宣示格式。作者只需求自定义一个与NetworkMessageDelegate同样格式的措施。看看NetworkMessageDelegate的申明格式:

public delegate void NetworkMessageDelegate(Networking.NetworkMessage
netMsg)。这里又涉及到了四个类:
NetworkMessage,依稀中记得NetworkConnection的send方法也急需传递1个NetworkMessage对象。表达小编的大势没错,NetworkConnection的音信传递是因而NetworkMessage类来封装的。进度应该是特别明朗的。先构造多个NetworkMessage对象,将以此指标通过send发送出去,在另1端用注册的主意接收NetworkMessage对象,最终深入分析成本人索要的消息。那么接下去最终一步–驾驭NetworkMessage类,继续API…

   
接下去蒙受一个相比困难的主题材料—笔者找不到组织NetworkMessage对象的章程,API里不曾提供构造函数,也未尝找到任何有效的静态方法。计划将梦想依托到他的父类,在vs里双击选中NetworkMessage,F1贰转到定义。才开采她从未父类,几乎握草了,就像是走进了死胡同。那时只有拿出极端军火了,百度时而…
直接搜索NetworkMessage。在那篇文章中找到了章程。仔细斟酌了一下,开掘和自个儿的笔触大约如出壹辙。

    好了,最终来计算一下历程,先说服务器端:

1、
写2个Sever类,承袭自NetworkManager。把这几个类挂在1个空物体上,并在Inspector面板设置好参数

2、 写按键点击三个轩然大波,调用StartServer方法

3、 使用override关键字重写一些on开始的网络事件措施

四、 写三个sendMsg的主意,在格局体内调用NetworkConnection的send方法

 

五、 写2个public void XXX(NetworkMessage
netMsg)这种申明格局的不贰诀窍,管理收到的消息

六、
找3个得当的机遇,调用NetworkConnection的RegisterHandler方法,将第4步定义的点子注册进来

客户端:

 

一、 
写一个Client类,承袭自NetworkManager。把这些类挂在一个空物体上,并在Inspector面板设置好参数(注意,客户端的NetworkAddress应该填写服务器端的IP,注意保持端口一致)

二、  写开关点击八个风云,调用StartClient方法

三、  使用override关键字重写一些on开端的网络事件措施

4、  写两个sendMsg的不2诀要,在点子体内调用NetworkConnection的send方法

伍、  写三个public void XXX(NetworkMessage
netMsg)这种表明方式的办法,管理收到的音讯

陆、 
找三个方便的空子,调用NetworkConnection的RegisterHandler方法,将第4步定义的措施注册进来

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website