unity探索者之socket传输protobuf字节流(三)

版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/6986474.html 

上一篇讲到了数据的处理,这一篇主要讲使用多线程收发消息

  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处理消息的收发就基本结束了,但是,某些项目为了增强体验,可能还会增加断线重连的功能,这个功能会在下一篇讲到

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Unity是一款跨平台的游戏开发引擎,而Protobuf是一种用于序列化结构化数据的协议,Socket是一种在网络通信中使用的编程接口。 Unity中使用ProtobufSocket可以实现网络通信功能。首先,我们可以使用Protobuf来定义网络通信中传输的数据结构和协议格式。Protobuf支持在不同的编程语言间进行数据交换,因此很方便地在Unity中使用。 通过Socket编程接口,Unity可以实现与远程服务器或其他客户端之间的网络通信。使用SocketUnity可以发送和接收经由Protobuf序列化的数据,从而实现与其他设备或服务器的数据交换。 使用Unity+Protobuf+Socket,我们可以实现一些功能,如多人在线游戏、实时数据同步、远程调用等。例如,在多人在线游戏中,我们可以使用Socket建立客户端与服务器之间的连接,通过Protobuf序列化游戏数据,通过Socket传输数据到服务器或其他玩家端,从而实现玩家之间的实时交互。 总而言之,Unity+Protobuf+Socket的组合可以帮助我们实现高效的网络通信,并且为游戏开发提供了很多有用的工具和功能。 ### 回答2: Unity是一款强大的游戏开发引擎,能够创建各种类型的游戏和应用程序。ProtobufProtocol Buffers)是一种高效的数据序列化和传输格式,它可以将结构化数据转换成二进制形式,使得数据的传输和存储更加高效。Socket则是一种用于在计算机网络中进行进程间通信的一种方式。 在Unity中使用ProtobufSocket可以实现高效的网络通信。首先,我们可以使用Protobuf来定义数据的结构和格式,通过定义消息的字段和类型,可以在客户端和服务器端之间传输和解析数据。Protobuf不仅提供了简洁的消息描述语言,还提供了代码生成器来生成针对不同编程语言的序列化和反序列化代码。 然后,我们可以使用Socket来建立网络连接,并通过发送和接收数据来进行通信。Socket可以实现同步和异步的通信方式,可以在客户端和服务器端之间实时地传输数据。通过将Protobuf序列化的数据通过Socket发送,接收方可以解析并处理这些数据。 Unity中的Socket编程可以使用C#的Socket类库来实现。通过创建socket对象,可以实现网络连接的建立和维护。然后可以使用Socket发送和接收数据,通过将Protobuf序列化的数据进行编码和解码,从而实现数据的传输和解析。 总结起来,Unity中使用ProtobufSocket可以实现高效的网络通信,通过定义消息的结构、通过Socket建立连接、通过Protobuf进行数据的序列化和反序列化,可以实现客户端和服务器端之间的实时通信。这种方式能够提高网络传输的效率,减少带宽和资源的消耗,使得游戏和应用程序具有更好的性能和用户体验。 ### 回答3: Unity是一款跨平台的游戏开发引擎,Protobuf是Google开发的一种数据序列化的格式,而Socket是一种用于网络通信的API。 在Unity中使用Protobuf可以帮助我们更方便地处理网络数据的序列化和反序列化。Protobuf可以将结构化的数据转换为二进制格式,从而提高数据传输的效率和安全性。Unity提供了对Protobuf的支持,我们可以在项目中导入Protobuf的文件,并使用Protobuf的工具生成相应的编解码类来进行数据的序列化和反序列化操作。 Socket则是网络编程中常用的通信方式之一,它提供了一种用于网络通信的API,可以实现不同设备之间的数据传输。在Unity中使用Socket可以实现客户端与服务器之间的实时通信。我们可以使用Socket来建立连接、发送和接收数据,并进行相应的处理。通过Socket的使用,可以在Unity中实现诸如多人游戏、聊天系统等功能。 在Unity中结合使用ProtobufSocket可以实现高效、安全的网络通信。我们可以使用Protobuf对数据进行序列化和反序列化操作,然后通过Socket进行数据的传输。这样可以确保数据的准确性和安全性。例如,在游戏中,我们可以使用Protobuf将玩家的位置信息、操作指令等数据序列化为二进制格式,然后通过Socket发送给服务器或其他玩家,再由接收端使用Socket进行数据的解析和处理。这样可以实现多人游戏中的实时同步,增加游戏的可玩性和互动性。 综上所述,Unity中的ProtobufSocket可以在游戏开发中起到重要的作用。Protobuf可以帮助我们方便地进行数据的序列化和反序列化操作,而Socket可以实现跨设备的网络通信。它们的结合使用可以实现高效、安全的网络通信,为游戏开发提供便利。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值