首页 > 代码库 > 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 }
--------------------------------------------------------------分割线-- 打个小广告-----------------------------------------------------------
女装饰品店:http://aoleitaisen.taobao.com
欢迎转载,转载必须保留
我的邮箱:zou90512@126.com 博客地址: http://www.cnblogs.com/zou90512
否则视为侵权
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。