Unity3D中简单的C#异步Socket实现
2014-08-12 14:50
246 查看
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字节)
上源码:注释的比较详细了。不明白的可以问我。
using System; using System.IO; using System.Net; using System.Net.Sockets; using Google.ProtocolBuffers; using MiscUtil.Conversion; // +------------------------+ // | Author : TinyZ | // | Data : 2014-08-12 | // |Ma-il : zou90512@126.com| // +------------------------+ // 注释一: 本例在编码上使用的是大端存贮,这个和C#本身是冲突的. 需要小端存储的朋友可以将MiscUtil的EndianBitConverter修改成.net提供的BitConverter // 注释二: 笔者这里使用了Protobuf协议. 所以写了一个工具在这里做转换使用. 大家可以直接删除Protobuf的那部分代码.不会对本例产生任何影响 // 注释三: 笔者这里实现了一个基于长度的解码器。用于避免粘包等问题。编码时候的长度描述数字的默认为short类型(长度2字节)。解码时候的长度描述数字默认为int类型(长度4字节) // 引用资料: // Miscellaneous Utility Library类库官网: http://www.yoda.arachsys.com/csharp/miscutil/ namespace Assets.TinyZ.Class.SimpleNet { /// <summary> /// 简单的异步Socket实现. 用于Unity3D客户端与JAVA服务端的数据通信. /// /// <br/><br/>方法:<br/> /// Connect:用于连接远程指定端口地址,连接成功后开启消息接收监听<br/> /// OnSendMessage:用于发送字节流消息. 长度不能超过short[65535]的长度<br/> /// <br/>事件:<br/> /// ReceiveMessageCompleted: 用于回调. 返回接收到的根据基于长度的解码器解码之后获取的数据[字节流] /// /// <br/><br/> /// [*]完全不支持C#等小端(Little Endian)编码 /// <br/><br/> /// 服务器为JAVA开发。因此编码均为 BigEndian编码 /// 消息的字节流格式如下:<br/> /// * +------------+-------------+ <br/> /// * |消息程度描述| 内容 | <br/> /// * | 0x04 | ABCD | <br/> /// * +------------+-------------+ <br/> /// 注释: 消息头为消息内容长度描述,后面是相应长度的字节内容. /// 由于是大端存储.所以无法使用C#提供的<see cref="BitConverter"/>进行解码. /// 本例使用的是网络开源MiscUtil中的大端转换器<see cref="EndianBitConverter"/> /// <br/><br/> /// </summary> /// <example> /// <code> /// // Unity3D客户端示例代码如下: /// var _simpleSocket = new SimpleSocket(); /// _simpleSocket.Connect("127.0.0.1", 9003); /// _simpleSocket.ReceiveMessageCompleted += (s, e) => /// { /// var rmc = e as ReceiveMessageCompletedEvent; /// if (rmc == null) return; /// var data = rmc.MessageData as byte[]; /// if (data != null) /// { /// // 在Unity3D控制台输出接收到的UTF-8格式字符串 /// Debug.Log(Encoding.UTF8.GetString(data)); /// } // _count++; /// }; /// /// // Unity3D客户端发送消息: /// _simpleSocket.OnSendMessage(Encoding.UTF8.GetBytes("Hello World!")); /// </code> /// </example> public class SimpleSocket { #region Construct /// <summary> /// Socket /// </summary> private readonly Socket _socket; /// <summary> /// SimpleSocket的构造函数 /// </summary> public SimpleSocket() { _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); //_socket.Blocking = false; // ? } /// <summary> /// 初始化Socket, 并设置帧长度 /// </summary> /// <param name="encoderLengthFieldLength">编码是消息长度数字的字节数长度. 1:表示1byte 2:表示2byte[Short类型] 4:表示4byte[int类型] 8:表示8byte[long类型]</param> /// <param name="decoderLengthFieldLength">解码时消息长度数字的字节数长度. 1:表示1byte 2:表示2byte[Short类型] 4:表示4byte[int类型] 8:表示8byte[long类型]</param> public SimpleSocket(int encoderLengthFieldLength, int decoderLengthFieldLength) : this() { _encoderLengthFieldLength = encoderLengthFieldLength; _decoderLengthFieldLength = decoderLengthFieldLength; } #endregion #region Connect to remote host /// <summary> /// 是否连接状态 /// </summary> /// <see cref="Socket.Connected"/> public bool Connected { get { return _socket != null && _socket.Connected; } } /// <summary> /// 连接指定的远程地址 /// </summary> /// <param name="host">远程地址</param> /// <param name="port">端口</param> public void Connect(string host, int port) { _socket.BeginConnect(host, port, OnConnectCallBack, this); } /// <summary> /// 连接指定的远程地址 /// </summary> /// <param name="ipAddress">目标网络协议ip地址</param> /// <param name="port">目标端口</param> /// 查看:<see cref="IPAddress"/> public void Connect(IPAddress ipAddress, int port) { _socket.BeginConnect(ipAddress, port, OnConnectCallBack, this); } /// <summary> /// 连接端点 /// </summary> /// <param name="endPoint">端点, 标识网络地址</param> /// 查看:<see cref="EndPoint"/> public void Connect(EndPoint endPoint) { _socket.BeginConnect(endPoint, OnConnectCallBack, this); } /// <summary> /// 连接的回调函数 /// </summary> /// <param name="ar"></param> private void OnConnectCallBack(IAsyncResult ar) { if (!_socket.Connected) return; _socket.EndConnect(ar); StartReceive(); } #endregion #region Send Message /// <summary> /// 编码时长度描述数字的字节长度[default = 2 => 65535字节] /// </summary> private readonly int _encoderLengthFieldLength = 2; /// <summary> /// 发送消息 /// </summary> /// <param name="data">要传递的消息内容[字节数组]</param> public void OnSendMessage(byte[] data) { var stream = new MemoryStream(); switch (_encoderLengthFieldLength) { case 1: stream.Write(new[] { (byte)data.Length }, 0, 1); break; case 2: stream.Write(EndianBitConverter.Big.GetBytes((short)data.Length), 0, 2); break; case 4: stream.Write(EndianBitConverter.Big.GetBytes(data.Length), 0, 4); break; case 8: stream.Write(EndianBitConverter.Big.GetBytes((long)data.Length), 0, 8); break; default: throw new Exception("unsupported decoderLengthFieldLength: " + _encoderLengthFieldLength + " (expected: 1, 2, 3, 4, or 8)"); } stream.Write(data, 0, data.Length); var all = stream.ToArray(); stream.Close(); _socket.BeginSend(all, 0, all.Length, SocketFlags.None, OnSendMessageComplete, all); } /// <summary> /// 发送消息完成的回调函数 /// </summary> /// <param name="ar"></param> private void OnSendMessageComplete(IAsyncResult ar) { SocketError socketError; _socket.EndSend(ar, out socketError); if (socketError != SocketError.Success) { _socket.Disconnect(false); throw new SocketException((int)socketError); } //Debug.Log("Send message successful !"); } #endregion #region Receive Message /// <summary> /// the length of the length field. 长度字段的字节长度, 用于长度解码 /// </summary> private readonly int _decoderLengthFieldLength = 4; /// <summary> /// 事件消息接收完成 /// </summary> public event EventHandler ReceiveMessageCompleted; /// <summary> /// 开始接收消息 /// </summary> private void StartReceive() { if (!_socket.Connected) return; var buffer = new byte[_decoderLengthFieldLength]; _socket.BeginReceive(buffer, 0, _decoderLengthFieldLength, SocketFlags.None, OnReceiveFrameLengthComplete, buffer); } /// <summary> /// 实现帧长度解码.避免粘包等问题 /// </summary> private void OnReceiveFrameLengthComplete(IAsyncResult ar) { var frameLength = (byte[]) ar.AsyncState; // 帧长度 var length = EndianBitConverter.Big.ToInt32(frameLength, 0); var data = new byte[length]; _socket.BeginReceive(data, 0, length, SocketFlags.None, OnReceiveDataComplete, data); } /// <summary> /// 数据接收完成的回调函数 /// </summary> private void OnReceiveDataComplete(IAsyncResult ar) { _socket.EndReceive(ar); var data = ar.AsyncState as byte[]; // 触发接收消息事件 if (ReceiveMessageCompleted != null) { ReceiveMessageCompleted(this, new ReceiveMessageCompletedEvent(data)); } StartReceive(); } #endregion #region Protocol Buffers Utility /// <summary> /// 发送消息 /// </summary> /// <typeparam name="T">IMessageLite的子类</typeparam> /// <param name="generatedExtensionLite">消息的扩展信息</param> /// <param name="messageLite">消息</param> public void OnSendMessage<T>(GeneratedExtensionLite<ServerMessage, T> generatedExtensionLite, T messageLite) where T : IMessageLite { var data = ConvertMessageToByteArray(generatedExtensionLite, messageLite); OnSendMessage(data); } /// <summary> /// Message转换为byte[] /// </summary> /// <typeparam name="T"></typeparam> /// <param name="generatedExtensionLite"></param> /// <param name="messageLite"></param> /// <returns></returns> public static byte[] ConvertMessageToByteArray<T>(GeneratedExtensionLite<ServerMessage, T> generatedExtensionLite, T messageLite) where T : IMessageLite { ServerMessage.Builder builder = ServerMessage.CreateBuilder(); builder.SetMsgId("" + generatedExtensionLite.Number); builder.SetExtension(generatedExtensionLite, messageLite); ServerMessage serverMessage = builder.Build(); return serverMessage.ToByteArray(); } /// <summary> /// byte[]转换为Message /// </summary> /// <typeparam name="T"></typeparam> /// <param name="data"></param> /// <param name="generatedExtensionLite"></param> /// <returns></returns> public static IMessageLite ConvertByteArrayToMessage<T>(byte[] data, GeneratedExtensionLite<ServerMessage, T> generatedExtensionLite) where T : IMessageLite { ExtensionRegistry extensionRegistry = ExtensionRegistry.CreateInstance(); extensionRegistry.Add(ProtobufMsgEnterGame.MsgEnterGame); extensionRegistry.Add(ProtobufMsgLogin.MsgLogin); extensionRegistry.Add(MsgBuyItem.msgBuyItem); ServerMessage serverMessage = ServerMessage.ParseFrom(data, extensionRegistry); return serverMessage.HasExtension(generatedExtensionLite) ? serverMessage.GetExtension(generatedExtensionLite) : default(T); } #endregion } #region Event /// <summary> /// 消息接收完成事件 /// </summary> public class ReceiveMessageCompletedEvent : EventArgs { /// <summary> /// 接收到的数据 /// </summary> private readonly object _data; public ReceiveMessageCompletedEvent(object data) { _data = data; } /// <summary> /// 消息数据 /// </summary> public object MessageData { get { return _data; } } } #endregion }
View Code
ps:
由于当初写这个代码的时候比较粗糙。笔者觉得新开一章发布版本1.1的。优化了一下以前的代码。推荐使用新的
直接上连接:
简单的异步Socket实现——SimpleSocket_V1.1
--------------------------------------------------------------分割线-- 打个小广告-----------------------------------------------------------女装饰品店:http://aoleitaisen.taobao.com
欢迎转载,转载必须保留
我的邮箱:zou90512@126.com博客地址:http://www.cnblogs.com/zou90512
否则视为侵权
相关文章推荐
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(二)----使用方法
- C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(二)
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(二)----使用方法
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架
- C#中使用异步Socket编程实现TCP网络服务的CS的通讯构架(一)----基础类库部分
- C# 实现的多线程异步Socket数据包接收器框架
- [转]在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(二)----使用方法
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分(来源:http://blog.csdn.net/yangjundeng/archive/2005/03/17/321920.aspx)
- C#实现Socket传输简单数据
- C# 实现的多线程异步Socket数据包接收器框架
- C# 实现的多线程异步Socket数据包接收器框架
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分
- C# 实现的多线程异步Socket数据包接收器框架
- C# 实现的多线程异步Socket数据包接收器框架(来源http://www.cnblogs.com/wcfgroup/archive/2008/10/06/1304512.html)
- C# 实现的多线程异步Socket数据包接收器框架
- 在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架
- [转]在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(二)