首页 > 代码库 > Unity3D中简单的C#异步Socket实现

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 : zou90512@126.com| 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 = http://www.mamicode.com/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 host101 102         /// <summary>103         /// 是否连接状态104         /// </summary>105         /// <see cref="Socket.Connected"/>106         public bool Connected107         {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         #endregion155 156 157         #region Send Message158 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         #endregion212 213 214         #region Receive Message215 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 = http://www.mamicode.com/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 = http://www.mamicode.com/ar.AsyncState as byte[];255             // 触发接收消息事件256             if (ReceiveMessageCompleted != null)257             {258                 ReceiveMessageCompleted(this, new ReceiveMessageCompletedEvent(data));259             }260             StartReceive();261         }262 263         #endregion264 265 266         #region Protocol Buffers Utility267 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 : IMessageLite276         {277             var data =http://www.mamicode.com/ 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 : IMessageLite289         {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[]转换为Message299         /// </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 : IMessageLite305         {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         #endregion318     }319 320     #region Event321 322     /// <summary>323     /// 消息接收完成事件324     /// </summary>325     public class ReceiveMessageCompletedEvent : EventArgs326     {327         /// <summary>328         /// 接收到的数据329         /// </summary>330         private readonly object _data;331 332         public ReceiveMessageCompletedEvent(object data)333         {334             _data =http://www.mamicode.com/ data;335         }336 337         /// <summary>338         /// 消息数据339         /// </summary>340         public object MessageData341         {342             get { return _data; }343         }344     }345 346     #endregion347 }
View Code

 

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

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

 

欢迎转载,转载必须保留

我的邮箱:zou90512@126.com 博客地址: http://www.cnblogs.com/zou90512

否则视为侵权