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 = ; /// <summary>
/// 发送消息
/// </summary>
/// <param name="data">要传递的消息内容[字节数组]</param>
public void OnSendMessage(byte[] data)
{
var stream = new MemoryStream();
switch (_encoderLengthFieldLength)
{
case :
stream.Write(new[] { (byte)data.Length }, , );
break;
case :
stream.Write(EndianBitConverter.Big.GetBytes((short)data.Length), , );
break;
case :
stream.Write(EndianBitConverter.Big.GetBytes(data.Length), , );
break;
case :
stream.Write(EndianBitConverter.Big.GetBytes((long)data.Length), , );
break;
default:
throw new Exception("unsupported decoderLengthFieldLength: " + _encoderLengthFieldLength + " (expected: 1, 2, 3, 4, or 8)");
}
stream.Write(data, , data.Length);
var all = stream.ToArray();
stream.Close();
_socket.BeginSend(all, , 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 = ; /// <summary>
/// 事件消息接收完成
/// </summary>
public event EventHandler ReceiveMessageCompleted; /// <summary>
/// 开始接收消息
/// </summary>
private void StartReceive()
{
if (!_socket.Connected) return;
var buffer = new byte[_decoderLengthFieldLength];
_socket.BeginReceive(buffer, , _decoderLengthFieldLength, SocketFlags.None, OnReceiveFrameLengthComplete, buffer);
} /// <summary>
/// 实现帧长度解码.避免粘包等问题
/// </summary>
private void OnReceiveFrameLengthComplete(IAsyncResult ar)
{
var frameLength = (byte[]) ar.AsyncState;
// 帧长度
var length = EndianBitConverter.Big.ToInt32(frameLength, );
var data = new byte[length];
_socket.BeginReceive(data, , 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
}

ps:

由于当初写这个代码的时候比较粗糙。笔者觉得新开一章发布版本1.1的。优化了一下以前的代码。推荐使用新的

直接上连接:

简单的异步Socket实现——SimpleSocket_V1.1

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

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

欢迎转载,转载必须保留

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

否则视为侵权

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

  1. 【Unity Shaders】Reflecting Your World —— Unity3D中简单的Cubemap反射

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  2. GJM :异步Socket [转载]

    原帖地址:http://blog.csdn.net/awinye/article/details/537264 原文作者:Awinye 目录(?)[-] 转载请原作者联系 Overview of So ...

  3. Unity3D中的常用方法

    备注:文中所使用的this均指脚本所依附的对象 1.移动(用Translate方法进行移动) ; //移动速度 this.transform.Translate(Vector3.down * Time ...

  4. 简单的异步Socket实现——SimpleSocket_V1.1

    简单的异步Socket实现——SimpleSocket_V1.1 笔者在前段时间的博客中分享了一段简单的异步.net的Socket实现.由于是笔者自己测试使用的.写的很粗糙.很简陋.于是花了点时间自己 ...

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

    公司今年3D产品的工作中心主要集中在提高产品深度上,通过对竞争产品的分析,发现我们的缺陷在于多人在线与后台管理部分,多人在线使用unity自带的Network能够搞定,后台部分前段时间主要研究了下Sq ...

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

    在刚刚开发Unity项目的过程中,需要用到即时通信功能来完成服务器与客户端自定义的数据结构封装. 现在将部分主要功能的实现代码抽取出来实现了可以异步Socket请求的技术Demo. 客户端脚本Clie ...

  7. 《Unity 3D游戏客户端基础框架》多线程异步 Socket 框架构建

    引言: 之前写过一个 demo 案例大致讲解了 Socket 通信的过程,并和自建的服务器完成连接和简单的数据通信,详细的内容可以查看 Unity3D -- Socket通信(C#).但是在实际项目应 ...

  8. Python简易聊天工具-基于异步Socket通信

    继续学习Python中,最近看书<Python基础教程>中的虚拟茶话会项目,觉得很有意思,自己敲了一遍,受益匪浅,同时记录一下. 主要用到异步socket服务客户端和服务器模块asynco ...

  9. 项目笔记---C#异步Socket示例

    概要 在C#领域或者说.net通信领域中有着众多的解决方案,WCF,HttpRequest,WebAPI,Remoting,socket等技术.这些技术都有着自己擅长的领域,或者被合并或者仍然应用于某 ...

随机推荐

  1. a<<=n

    a<<=n等价于a=a<<na<<n表示a左移n位(二进制)等价于a乘以2的n次方 a<<=n的含义就是,a等于a乘以2的n次方

  2. Android colors.xml 颜色列表

    android 常用项 <?xml version="1.0" encoding="utf-8"?> <resources> <c ...

  3. vb.net小试三层架构

    在对三层架构有了初步了解后,用vb.net做了一个小的程序,真的很小,仅仅是为了体现一下三层之间机制.下面是我设计的操作界面: 还有程序集和类的分布情况, 接下来是数据的设计,数据库用到的是SQL S ...

  4. Bitmap和Drawable的互相转换

    刚好之前的项目实用到.怕遗忘了.就先记录下来.然后会用到的时候直接来这copy使用就好了. 1.Bitmap ---->Drawable: public static Drawable bitm ...

  5. 禁止 git 自动转换换行符

    开发团队都在 windows 下开发,有IDE管理代码.对我们来说,最好是禁用换行转换符的功能.我用 cygwin 提交代码,提交时总提示自动转换换符.其实都不用提交,仅运行 git status 看 ...

  6. JavaScript | window浏览器对象模型

    Js Window - 获取浏览器窗口 全局变量是window对象的属性 全局函数是window对象的方法 HTML DOM的document是window对象属性之一 window.document ...

  7. VB.NET与 sql数据库

    数据蕴含丰富的信息,数据就是资源. 不同的语言,因为各自的语法特点.对sql数据库的连接操作有些小差别.但有一点,那就是.对sql数据库的操作语句sql语句大体是一样的. 这段时间正进行VB.NET的 ...

  8. sql 数据类型 论可变长度字符串与定长性能差异(my sql版)

    首先从字节上来说CHAR是定长,意思就是只要输入在我这个定长以下,不管是几个字符,它的实际占用空间都是CHAR定长的长度.而VARCHAR则相对来说会节省一点空间,比如:你VARCHAR的长度设为10 ...

  9. 1 bootstrap table null默认显示为 - 要查源码 2 记一个很无语的bug

    本来返回的json 3个true 7个false的 结果显示10个true 因为本来是好的 结果判断的问题 给全部赋值true了

  10. SSL and SSL Certificates Explained

    Secure Sockets Layer (SSL) and Transport Layer security (TLS ) are protocols that provide secure com ...