Unity3D中简单的C#异步Socket实现

Unity3D中简单的C#异步Socket实现

  简单的异步Socket实现。.net框架自身提供了很完善的Socket底层。笔者在做Unity3D小东西的时候需要使用到Socket网络通信。于是决定自己研究研究。

  经过不懈努力。。O(∩_∩)O哈哈~。。自我夸奖一下。终于搞定了。SimpleSocket.cs

  由于笔者本身并不是专业的C#程序员。O(∩_∩)O哈哈~。大神就可以直接忽视这篇文章了。顾名思义。哈哈简单的Socket。给那些没接触的盆友参考借鉴下吧。服务社会了

  

  注释一: 本例在编码上使用的是大端存贮,这个和C#本身是冲突的. 需要小端存储的朋友可以将MiscUtil的EndianBitConverter修改成.net提供的BitConverter

  注释二: 笔者这里使用了Protobuf协议. 所以写了一个工具在这里做转换使用. 大家可以直接删除Protobuf的那部分代码.不会对本例产生任何影响

  注释三:笔者这里实现了一个基于长度的解码器。用于避免粘包等问题。编码时候的长度描述数字的默认为short类型(长度2字节)。解码时候的长度描述数字默认为int类型(长度4字节)

  上源码:注释的比较详细了。不明白的可以问我。

  1 using System;
  2 using System.IO;
  3 using System.Net;
  4 using System.Net.Sockets;
  5 using Google.ProtocolBuffers;
  6 using MiscUtil.Conversion;
  7
  8 // +------------------------+
  9 // |    Author : TinyZ      |
 10 // |   Data : 2014-08-12    |
 11 // |Ma-il : [email protected]|
 12 // +------------------------+
 13 // 注释一: 本例在编码上使用的是大端存贮,这个和C#本身是冲突的. 需要小端存储的朋友可以将MiscUtil的EndianBitConverter修改成.net提供的BitConverter
 14 // 注释二: 笔者这里使用了Protobuf协议. 所以写了一个工具在这里做转换使用. 大家可以直接删除Protobuf的那部分代码.不会对本例产生任何影响
 15 // 注释三: 笔者这里实现了一个基于长度的解码器。用于避免粘包等问题。编码时候的长度描述数字的默认为short类型(长度2字节)。解码时候的长度描述数字默认为int类型(长度4字节)
 16 // 引用资料:
 17 // Miscellaneous Utility Library类库官网: http://www.yoda.arachsys.com/csharp/miscutil/
 18
 19 namespace Assets.TinyZ.Class.SimpleNet
 20 {
 21     /// <summary>
 22     /// 简单的异步Socket实现. 用于Unity3D客户端与JAVA服务端的数据通信.
 23     ///
 24     /// <br/><br/>方法:<br/>
 25     /// Connect:用于连接远程指定端口地址,连接成功后开启消息接收监听<br/>
 26     /// OnSendMessage:用于发送字节流消息. 长度不能超过short[65535]的长度<br/>
 27     /// <br/>事件:<br/>
 28     /// ReceiveMessageCompleted: 用于回调. 返回接收到的根据基于长度的解码器解码之后获取的数据[字节流]
 29     ///
 30     /// <br/><br/>
 31     /// [*]完全不支持C#等小端(Little Endian)编码
 32     /// <br/><br/>
 33     /// 服务器为JAVA开发。因此编码均为 BigEndian编码
 34     /// 消息的字节流格式如下:<br/>
 35     ///     * +------------+-------------+ <br/>
 36     ///     * |消息程度描述|  内容       | <br/>
 37     ///     * |    0x04    | ABCD        | <br/>
 38     ///     * +------------+-------------+ <br/>
 39     /// 注释: 消息头为消息内容长度描述,后面是相应长度的字节内容.
 40     /// 由于是大端存储.所以无法使用C#提供的<see cref="BitConverter"/>进行解码.
 41     /// 本例使用的是网络开源MiscUtil中的大端转换器<see cref="EndianBitConverter"/>
 42     /// <br/><br/>
 43     /// </summary>
 44     /// <example>
 45     /// <code>
 46     /// // Unity3D客户端示例代码如下:
 47     /// var _simpleSocket = new SimpleSocket();
 48     /// _simpleSocket.Connect("127.0.0.1", 9003);
 49     /// _simpleSocket.ReceiveMessageCompleted += (s, e) =>
 50     /// {
 51     ///     var rmc = e as ReceiveMessageCompletedEvent;
 52     ///     if (rmc == null) return;
 53     ///     var data = rmc.MessageData as byte[];
 54     ///     if (data != null)
 55     ///     {
 56     ///         // 在Unity3D控制台输出接收到的UTF-8格式字符串
 57     ///         Debug.Log(Encoding.UTF8.GetString(data));
 58     ///     }
 59     //      _count++;
 60     /// };
 61     ///
 62     /// // Unity3D客户端发送消息:
 63     /// _simpleSocket.OnSendMessage(Encoding.UTF8.GetBytes("Hello World!"));
 64     /// </code>
 65     /// </example>
 66     public class SimpleSocket
 67     {
 68         #region Construct
 69
 70         /// <summary>
 71         /// Socket
 72         /// </summary>
 73         private readonly Socket _socket;
 74
 75         /// <summary>
 76         /// SimpleSocket的构造函数
 77         /// </summary>
 78         public SimpleSocket()
 79         {
 80             _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 81             _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
 82             //_socket.Blocking = false; // ?
 83
 84         }
 85
 86         /// <summary>
 87         /// 初始化Socket, 并设置帧长度
 88         /// </summary>
 89         /// <param name="encoderLengthFieldLength">编码是消息长度数字的字节数长度. 1:表示1byte  2:表示2byte[Short类型] 4:表示4byte[int类型] 8:表示8byte[long类型]</param>
 90         /// <param name="decoderLengthFieldLength">解码时消息长度数字的字节数长度. 1:表示1byte  2:表示2byte[Short类型] 4:表示4byte[int类型] 8:表示8byte[long类型]</param>
 91         public SimpleSocket(int encoderLengthFieldLength, int decoderLengthFieldLength) : this()
 92         {
 93             _encoderLengthFieldLength = encoderLengthFieldLength;
 94             _decoderLengthFieldLength = decoderLengthFieldLength;
 95         }
 96
 97         #endregion
 98
 99
100         #region Connect to remote host
101
102         /// <summary>
103         /// 是否连接状态
104         /// </summary>
105         /// <see cref="Socket.Connected"/>
106         public bool Connected
107         {
108             get { return _socket != null && _socket.Connected; }
109         }
110
111         /// <summary>
112         /// 连接指定的远程地址
113         /// </summary>
114         /// <param name="host">远程地址</param>
115         /// <param name="port">端口</param>
116         public void Connect(string host, int port)
117         {
118             _socket.BeginConnect(host, port, OnConnectCallBack, this);
119         }
120
121         /// <summary>
122         /// 连接指定的远程地址
123         /// </summary>
124         /// <param name="ipAddress">目标网络协议ip地址</param>
125         /// <param name="port">目标端口</param>
126         /// 查看:<see cref="IPAddress"/>
127         public void Connect(IPAddress ipAddress, int port)
128         {
129             _socket.BeginConnect(ipAddress, port, OnConnectCallBack, this);
130         }
131
132         /// <summary>
133         /// 连接端点
134         /// </summary>
135         /// <param name="endPoint">端点, 标识网络地址</param>
136         /// 查看:<see cref="EndPoint"/>
137         public void Connect(EndPoint endPoint)
138         {
139             _socket.BeginConnect(endPoint, OnConnectCallBack, this);
140         }
141
142
143         /// <summary>
144         /// 连接的回调函数
145         /// </summary>
146         /// <param name="ar"></param>
147         private void OnConnectCallBack(IAsyncResult ar)
148         {
149             if (!_socket.Connected) return;
150             _socket.EndConnect(ar);
151             StartReceive();
152         }
153
154         #endregion
155
156
157         #region Send Message
158
159         /// <summary>
160         /// 编码时长度描述数字的字节长度[default = 2 => 65535字节]
161         /// </summary>
162         private readonly int _encoderLengthFieldLength = 2;
163
164         /// <summary>
165         /// 发送消息
166         /// </summary>
167         /// <param name="data">要传递的消息内容[字节数组]</param>
168         public void OnSendMessage(byte[] data)
169         {
170             var stream = new MemoryStream();
171             switch (_encoderLengthFieldLength)
172             {
173                 case 1:
174                     stream.Write(new[] { (byte)data.Length }, 0, 1);
175                     break;
176                 case 2:
177                     stream.Write(EndianBitConverter.Big.GetBytes((short)data.Length), 0, 2);
178                     break;
179                 case 4:
180                     stream.Write(EndianBitConverter.Big.GetBytes(data.Length), 0, 4);
181                     break;
182                 case 8:
183                     stream.Write(EndianBitConverter.Big.GetBytes((long)data.Length), 0, 8);
184                     break;
185                 default:
186                     throw new Exception("unsupported decoderLengthFieldLength: " + _encoderLengthFieldLength + " (expected: 1, 2, 3, 4, or 8)");
187             }
188             stream.Write(data, 0, data.Length);
189             var all = stream.ToArray();
190             stream.Close();
191             _socket.BeginSend(all, 0, all.Length, SocketFlags.None, OnSendMessageComplete, all);
192         }
193
194         /// <summary>
195         /// 发送消息完成的回调函数
196         /// </summary>
197         /// <param name="ar"></param>
198         private void OnSendMessageComplete(IAsyncResult ar)
199         {
200             SocketError socketError;
201             _socket.EndSend(ar, out socketError);
202             if (socketError != SocketError.Success)
203             {
204                 _socket.Disconnect(false);
205                 throw new SocketException((int)socketError);
206             }
207             //Debug.Log("Send message successful !");
208         }
209
210
211         #endregion
212
213
214         #region Receive Message
215
216         /// <summary>
217         /// the length of the length field. 长度字段的字节长度, 用于长度解码
218         /// </summary>
219         private readonly int _decoderLengthFieldLength = 4;
220
221         /// <summary>
222         /// 事件消息接收完成
223         /// </summary>
224         public event EventHandler ReceiveMessageCompleted;
225
226         /// <summary>
227         /// 开始接收消息
228         /// </summary>
229         private void StartReceive()
230         {
231             if (!_socket.Connected) return;
232             var buffer = new byte[_decoderLengthFieldLength];
233             _socket.BeginReceive(buffer, 0, _decoderLengthFieldLength, SocketFlags.None, OnReceiveFrameLengthComplete, buffer);
234         }
235
236         /// <summary>
237         /// 实现帧长度解码.避免粘包等问题
238         /// </summary>
239         private void OnReceiveFrameLengthComplete(IAsyncResult ar)
240         {
241             var frameLength = (byte[]) ar.AsyncState;
242             // 帧长度
243             var length = EndianBitConverter.Big.ToInt32(frameLength, 0);
244             var data = new byte[length];
245             _socket.BeginReceive(data, 0, length, SocketFlags.None, OnReceiveDataComplete, data);
246         }
247
248         /// <summary>
249         /// 数据接收完成的回调函数
250         /// </summary>
251         private void OnReceiveDataComplete(IAsyncResult ar)
252         {
253             _socket.EndReceive(ar);
254             var data = ar.AsyncState as byte[];
255             // 触发接收消息事件
256             if (ReceiveMessageCompleted != null)
257             {
258                 ReceiveMessageCompleted(this, new ReceiveMessageCompletedEvent(data));
259             }
260             StartReceive();
261         }
262
263         #endregion
264
265
266         #region Protocol Buffers Utility
267
268         /// <summary>
269         /// 发送消息
270         /// </summary>
271         /// <typeparam name="T">IMessageLite的子类</typeparam>
272         /// <param name="generatedExtensionLite">消息的扩展信息</param>
273         /// <param name="messageLite">消息</param>
274         public void OnSendMessage<T>(GeneratedExtensionLite<ServerMessage, T> generatedExtensionLite, T messageLite)
275             where T : IMessageLite
276         {
277             var data = ConvertMessageToByteArray(generatedExtensionLite, messageLite);
278             OnSendMessage(data);
279         }
280
281         /// <summary>
282         /// Message转换为byte[]
283         /// </summary>
284         /// <typeparam name="T"></typeparam>
285         /// <param name="generatedExtensionLite"></param>
286         /// <param name="messageLite"></param>
287         /// <returns></returns>
288         public static byte[] ConvertMessageToByteArray<T>(GeneratedExtensionLite<ServerMessage, T> generatedExtensionLite, T messageLite) where T : IMessageLite
289         {
290             ServerMessage.Builder builder = ServerMessage.CreateBuilder();
291             builder.SetMsgId("" + generatedExtensionLite.Number);
292             builder.SetExtension(generatedExtensionLite, messageLite);
293             ServerMessage serverMessage = builder.Build();
294             return serverMessage.ToByteArray();
295         }
296
297         /// <summary>
298         /// byte[]转换为Message
299         /// </summary>
300         /// <typeparam name="T"></typeparam>
301         /// <param name="data"></param>
302         /// <param name="generatedExtensionLite"></param>
303         /// <returns></returns>
304         public static IMessageLite ConvertByteArrayToMessage<T>(byte[] data, GeneratedExtensionLite<ServerMessage, T> generatedExtensionLite) where T : IMessageLite
305         {
306             ExtensionRegistry extensionRegistry = ExtensionRegistry.CreateInstance();
307             extensionRegistry.Add(ProtobufMsgEnterGame.MsgEnterGame);
308             extensionRegistry.Add(ProtobufMsgLogin.MsgLogin);
309             extensionRegistry.Add(MsgBuyItem.msgBuyItem);
310
311             ServerMessage serverMessage = ServerMessage.ParseFrom(data, extensionRegistry);
312             return serverMessage.HasExtension(generatedExtensionLite)
313                 ? serverMessage.GetExtension(generatedExtensionLite)
314                 : default(T);
315         }
316
317         #endregion
318     }
319
320     #region Event
321
322     /// <summary>
323     /// 消息接收完成事件
324     /// </summary>
325     public class ReceiveMessageCompletedEvent : EventArgs
326     {
327         /// <summary>
328         /// 接收到的数据
329         /// </summary>
330         private readonly object _data;
331
332         public ReceiveMessageCompletedEvent(object data)
333         {
334             _data = data;
335         }
336
337         /// <summary>
338         /// 消息数据
339         /// </summary>
340         public object MessageData
341         {
342             get { return _data; }
343         }
344     }
345
346     #endregion
347 }

--------------------------------------------------------------分割线-- 打个小广告-----------------------------------------------------------

女装饰品店:http://aoleitaisen.taobao.com

欢迎转载,转载必须保留

我的邮箱:[email protected] 博客地址: http://www.cnblogs.com/zou90512

否则视为侵权

Unity3D中简单的C#异步Socket实现

时间: 2024-10-14 14:38:54

Unity3D中简单的C#异步Socket实现的相关文章

Unity3d 游戏与C#服务器 异步Socket 交互 (一)

Unity3d中提供了Socket供开发者使用,语法和.net中的一致. 一般来说,对于手游客户端,分为两个线程,一个是GLES渲染,另一个就是Socket线程了. 文章转自(http://blog.csdn.net/huutu) 不论是服务器,还是客户端.其间的数据包的接收与发送,都是通过Socket. 比如客户端要登录,我们就新建一个Socket,Connect到帐号服务器.帐号服务器一直在等待客户端的连接,客户端连接进来之后就准备发送接收数据包了. 文章转自(http://blog.csd

Windows 8 Metro 关于StreamSocket与原异步Socket

前一篇 <Windows 8 Metro 关于 StreamSocket MSDN示例阅读>我们基本懂得如何通过StreamSocket 做监听.连接.发送接收数据. 同时前一篇留下的几个疑问,我们在这里进行实验和解答. 在“原有的异步Socket”连接方式与现在WIN8 Metro App 的StreamSocket 如何通信呢? 首先解释下这里说的“原有的异步Socket” 是什么. 在await/async 关键字出现前,我们的Socket异步是依靠System.Net 以及 Syste

unity3D中使用Socket进行数据通信(一)

公司今年3D产品的工作中心主要集中在提高产品深度上,通过对竞争产品的分析,发现我们的缺陷在于多人在线与后台管理部分,多人在线使用unity自带的Network能够搞定,后台部分前段时间主要研究了下Sqlite.因为sqlite本身不带server,作为数据库仅仅是一个文件,sqlite放在server上与client通信的需求事实上就是多个client訪问同一个文件了.查了一些资料,有非常多方法,个人感觉socket不错.一来应用广泛.自己学习后也是一个充电,而来操作灵活,比作文件共享更自由.所

Unity3d中SendMessage 用法简单笔记

Unity3d中SendMessage 用法简单笔记 Message相关有3条指令:SendMessage ("函数名",参数,SendMessageOptions) //GameObject自身的ScriptBroadcastMessage ("函数名",参数,SendMessageOptions)  //自身和子Object的ScriptSendMessageUpwards ("函数名",参数,SendMessageOptions)  //自

unity3d中实用数据库简单入门

入门笔记,大拿出门左拐. unity3d中实用简单的数据库入门. 声明3变量 string connection; //数据库的位置名字信息 IDbConnection _dbconnect; //可以把他理解成数据库的连接 IDbCommand _dbcmd; //cmd命令,sql语句主要在这里些 然后可以把操作数据库放在一个类构造里或者start,只要不是update里就行里. connection = "URI=file://Assets//ly//test.db"; _dbc

python中asynchat异步socket命令/响应处理

该模块基于asyncore简化了异步客户端和服务器,并使其更容易元素处理由任意的字符串结束,或者是可变长度的的协议.它提供了抽象类async_chat,提供collect_incoming_data()和found_terminator()方法.循环和asyncore的一样,有2种信道:asyncore.dispatcher和asynchat.async_chat,可以自由混合信道.通常asyncore.dispatcher服务器通道在接收到连接请求时产生新的asynchat.async_cha

【Unity Shaders】Reflecting Your World —— 在Unity3D中创建一个简单的动态Cubemap系统

本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源(当然你也可以从官网下载). ========================================== 分割线 ========================================== 写在前面 我们已经学了很多关于反射的内容,但是我们现在的反射并不能实时反射,即当反射物体移动时它们不

[Unity Socket]在Unity中如何实现异步Socket通信技术

在刚刚开发Unity项目的过程中,需要用到即时通信功能来完成服务器与客户端自定义的数据结构封装. 现在将部分主要功能的实现代码抽取出来实现了可以异步Socket请求的技术Demo. 客户端脚本ClientScript /// <summary> /// Client Script. /// Created By 蓝鸥3G 2014.08.23 /// </summary> using UnityEngine; using System.Collections; public cla

在Unity3d中解析Lua脚本的方法

由于近期项目中提出了热更新的需求,因此本周末在Lua的陪伴下度过.对Lua与Unity3d的搭配使用,仅仅达到了一个初窥门径的程度,记录一二于此.水平有限,欢迎批评指正. 网络上关于Lua脚本和Unity3d的配合使用的资料不多,例子工程大多相同.大概了解到针对性的插件有uLua.UniLua.KopiLua三种.试用了前两种,抛开效率与安全性不说,感觉uLua试用起来比较简单,本文只介绍uLua的使用步骤. uLua的原理是在Unity3d中解析字符串形式的Lua脚本,让Lua与C#相互传递参