一、前言

AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台。用于帮助中小型软件企业建立一条适合市场快速变化的开发团队,以达到节省开发成本、缩短开发时间,快速适应市场变化的目的。

AgileEAS.NET SOA中间件平台提供了敏捷快速开发软件工程的最佳实践,通过提供大量的基础支撑功能如IOC、ORM、SOA、分布式体系及敏捷并发开发方法所支撑的插件开发体系,以及提供了大量的实体、数据模型设计生成工具、代码生成工具,用于帮助中小软件开发商快速成长。

AgileEAS.NET平台充分把握目前软件行业快速发展的新趋势,基于敏捷并行开发、快速适应市场这样淳朴的软件工程实践,采用业界广泛使用的Microsoft .Net构件(组件)开发技术实践了这种开发思想,帮助软件企业实现“敏捷变化、快速适合”的目标,从而帮助软件企业在激烈的市场竞争中赢得先机并获得更高的回报。

二、关于Socket/Tcp框架的需求

在AgileEAS.NET SOA 中间件平台在大量客户的使用过程之中,有的客户提出了一些基于“消息推”、和应用系统事件通知的需求,比如在“医院信息系统”、“电子病历系统‘、”区域公共卫生平台“、等系统之中就提供了这样的需求,比如当大夫为病人开立了医嘱之后、需要在相关护理人员即时提示,之前大家都使用数据库刷新,数据库的压力都比较大,所以迫切的提出了这样的需求。

另外一个原因是AgileEAS.NET SOA 中间件平台之前一直使用WCF、WS、Romotinig通信做为SOA分布式架构的通信基础,我们在考虑WS、WCF的某些协议并不能提供给我们非常高效的通信,所以我们也需要有一套直接基于Socket/TCP的通信体系用于支撑我们的SOA分布式服务业务体系。

三、AgileEAS.NET SOA 中间件Socket/Tcp框架结构

AgileEAS.NET SOA中间件需要的Socket/Tcp框架严格意思是需要的基于Socket的通信消息中件间,其所提供的功能本质是对消息的处理,所以其所提供的API有三大类、Socket框架本身、消息框架及消息处理框架,以下是AgileEAS.NET SOA中间件的Socket/Tcp的应用框架结构:

各上图我们可以看到AgileEAS.NET SOA中间件需要的Socket/Tcp框架实现了这么一件有意思的事,把原生的Socekt基于数据流的流式通信模式转换为基于消息的消息通信模式,让开发人员多复杂的系统Socket通信和数据流处理之中解放出来,转而关心高层通信消息的设计和消息处理的业务设计与实现。

三、AgileEAS.NET SOA 中间件Socket/Tcp框架重要的接口和类

AgileEAS.NET SOA中间件需要的Socket/Tcp框架严格意思是需要的基于Socket的通信消息中件间,其所提供的功能本质是对消息的处理,所以其所提供的API有三大类、Socket框架本身、消息框架及消息处理框架,以下是AgileEAS.NET SOA中间件的Socket/Tcp的应用框架结构:

其中ISocketCient接口为客户段功能封装,其定义大体如下:

   1: /// <summary>

   2: /// Tcp客户端接口。

   3: /// </summary>

   4: public interface ISocketClient : ISocketEngine

   5: {

   6:     /// <summary>

   7:     /// 客户端状态。

   8:     /// </summary>

   9:     ClientState ClientState

  10:     {

  11:         get;

  12:     }        

  13:  

  14:     /// <summary>

  15:     /// 连接Tcp服务器。

  16:     /// </summary>

  17:     bool Connect();

  18:  

  19:     /// <summary>

  20:     /// 关闭与服务器的连接。

  21:     /// </summary>

  22:     void Close();

  23:  

  24:     /// <summary>

  25:     /// 发送消息。

  26:     /// </summary>

  27:     /// <param name="message">消息。</param>

  28:     void Send(IMessage message);

  29:  

  30:     /// <summary>

  31:     /// 调用消息,用于服务器/客户端应用中的请示==》响应消息的应用。

  32:     /// </summary>

  33:     /// <param name="request">请示消息。</param>

  34:     /// <returns>服务器返回的响应消息。</returns>

  35:     IMessage Invoke(IMessage request);

  36:  

  37:     /// <summary>

  38:     /// 异步调用消息,用于服务器/客户端应用中的请示==》响应消息的应用。

  39:     /// </summary>

  40:     /// <param name="request">请示消息。</param>

  41:     /// <returns>包含响应消息的异步任务。</returns>

  42:     NetInvokeTask BeginInvoke(IMessage request);

  43:  

  44:     /// <summary>

  45:     /// 服务器发生错误时触发。

  46:     /// </summary>

  47:     event ErrorEventHandler Error;

  48:  

  49:     /// <summary>

  50:     /// 连接服务器后触发。

  51:     /// </summary>

  52:     event EventHandler Connected;

  53:  

  54:     /// <summary>

  55:     /// 断开服务器连接后触发。

  56:     /// </summary>

  57:     event EventHandler Closed;

  58: }

其是最重要方法为void Send(IMessage message)方法,即发送一个消息到服务器,这个方法也是对使用者开放的最重要方法,在这里发送的不是字节流,而是一个实现了IMessage接口的消息对象,当服户段接收到IMessage对象之后会调用与其对应的消息处理器(IMessageHandler)对象进行消息处理,反之客户段收到IMessage也会调用与其相关的消息处理器(IMessageHandler)对象进行处理。

ISocketServer、ISocketServerBase接口:

   1: /// <summary>

   2: /// Socket服务器基类接口。

   3: /// </summary>

   4: public interface ISocketServerBase : ISocketEngine

   5: {

   6:     /// <summary>

   7:     /// 客户端会话集合。

   8:     /// </summary>

   9:     IList<NetSession> Sessions

  10:     {

  11:         get;

  12:     }

  13:  

  14:     /// <summary>

  15:     /// 注册了一个新会话后发生。

  16:     /// </summary>

  17:     event NetSessionEventHandler SessionStarted;

  18:  

  19:     /// <summary>

  20:     /// 某一个会话结束后发生。

  21:     /// </summary>

  22:     event NetSessionEventHandler SessionAbandoned;

  23:  

  24:     /// <summary>

  25:     /// 发送消息。

  26:     /// </summary>

  27:     /// <param name="target">消息接收方ID(会话ID)。</param>

  28:     /// <param name="message">消息。</param>

  29:     void Send(Guid target, IMessage message);

  30:  

  31:     /// <summary>

  32:     /// 发送网络消息。

  33:     /// </summary>

  34:     /// <param name="target">消息接收方ID(会话ID)。</param>

  35:     /// <param name="netMessage">网络消息。</param>

  36:     void Send(Guid target, NetMessage netMessage);

  37:  

  38:     /// <summary>

  39:     /// 发送网络报文(仅网关模式有效)。

  40:     /// </summary>

  41:     /// <param name="target">消息接收方ID(会话ID)。</param>

  42:     /// <param name="netPacket">网络报文。</param>

  43:     void Send(Guid target, NetPacket netPacket);

  44:  

  45:     /// <summary>

  46:     /// 发送网络数据(仅网关模式有效)。

  47:     /// </summary>

  48:     /// <param name="target">消息接收方ID(会话ID)。</param>

  49:     /// <param name="buffer">网络数据。</param>

  50:     void Send(Guid target, byte[] buffer);

  51: }

   1: /// <summary>

   2: /// Socket服务器接口。

   3: /// </summary>

   4: public interface ISocketServer :  ISocketServerBase

   5: {

   6:     /// <summary>

   7:     /// 客户端连接数。

   8:     /// </summary>

   9:     int ClientCount

  10:     {

  11:         get;

  12:     }

  13:  

  14:     /// <summary>

  15:     /// 服务器状态。

  16:     /// </summary>

  17:     ServerState ServerState

  18:     {

  19:         get;

  20:     }        

  21:  

  22:     /// <summary>

  23:     /// 开始Tcp服务器。

  24:     /// </summary>

  25:     void StartServer();

  26:  

  27:     /// <summary>

  28:     /// 停止Tcp服务器。

  29:     /// </summary>

  30:     void StopServer();

  31:  

  32:     /// <summary>

  33:     /// 关闭指定客户的连接。

  34:     /// </summary>

  35:     /// <param name="client">客户Guid。</param>

  36:     void AbandonSession(System.Guid client);

  37:  

  38:     /// <summary>

  39:     /// 服务器发生错误时触发。

  40:     /// </summary>

  41:     event ServerErrorEventHandler ServerError;

  42:  

  43:     /// <summary>

  44:     /// 服务器启动后触发。

  45:     /// </summary>

  46:     event System.EventHandler ServerStarted;        

  47:  

  48:     /// <summary>

  49:     /// 服务器停止后触发。

  50:     /// </summary>

  51:     event System.EventHandler ServerStopped;

  52: }

这两个接口定义了SocketServer的一些行为和属性,其中最重要的方法还是void Send(Guid target, IMessage message),实现向某个特定客户段连接发送应用消息,别外定义了一个IList<NetSession> Sessions属性,表示目前连接到此SocketServer的所有客户端会话信息。

NetSession表示服务器的一个客户段连接会话,包括连接上下文信息和连接的Socket通信对象,当某个SocketClient发送给SocketServer的信息都会被与其应对的NetSession进行处理,NetSession定义两个重要的方法public void Reply(uint requestID, IMessage message)和public void Abandon(),其中Reply表示向客户端回复一个消息,Abandon表示服务器强制中止此会话。

在整个Socket/tcp框架之中进行通信的最基本单元都是IMessage,那么SocketClient、SocketServer接收到IMessage如何处理呢,答案是由与之配对的IMessageHandler进行处理,所以SocketClient、SocketServer都实现了一个基础接口ISocketEngine:

   1: /// <summary>

   2: /// Socket引擎,Socket网络通信基础类。

   3: /// </summary>

   4: public interface ISocketEngine : IDisposable

   5: {

   6:     /// <summary>

   7:     /// 通信引擎的全局唯一标识符号。

   8:     /// </summary>

   9:     System.Guid Guid

  10:     {

  11:         get;

  12:     }

  13:  

  14:     /// <summary>

  15:     /// IP地址和端口号。

  16:     /// </summary>

  17:     IPEndPoint IPEndPoint

  18:     {

  19:         get;

  20:         set;

  21:     }

  22:  

  23:     /// <summary>

  24:     /// IP地址。

  25:     /// </summary>

  26:     string IPAddress

  27:     {

  28:         get;

  29:         set;

  30:     }

  31:  

  32:     /// <summary>

  33:     /// 端口号。

  34:     /// </summary>

  35:     int Port

  36:     {

  37:         get;

  38:         set;

  39:     }

  40:  

  41:     /// <summary>

  42:     /// 报文最大长度。

  43:     /// </summary>

  44:     int MessageMaxSize

  45:     {

  46:         get;

  47:     }        

  48:  

  49:     /// <summary>

  50:     /// 注册消息处理器。

  51:     /// </summary>

  52:     /// <typeparam name="T">消息类型。</typeparam>

  53:     /// <param name="hander">消息处理器。</param>

  54:     void AddHander<T>(IMessageHandler<T> hander) where T : IMessage;

  55:  

  56:     /// <summary>

  57:     /// 通过Socket发送数据之后触发。

  58:     /// </summary>

  59:     event SocketDataHandler SocketDataSend;

  60:  

  61:     /// <summary>

  62:     ///  通过Socket接收数据之后触发。

  63:     /// </summary>

  64:     event SocketDataHandler SocketDataReceived;

  65:  

  66:     /// <summary>

  67:     /// 发送报文完成之后触发。

  68:     /// </summary>

  69:     event PacketHandler PacketSend;

  70:  

  71:     /// <summary>

  72:     /// 报文接收完成之后触发。

  73:     /// </summary>

  74:     event PacketHandler PacketReceived;

  75:  

  76:     /// <summary>

  77:     /// 载送完一个NetMessage之后触发。

  78:     /// </summary>

  79:     event NetMessageHandler NetMessageSend;

  80:  

  81:     /// <summary>

  82:     /// 接收完一个NetMessage之后触发。

  83:     /// </summary>

  84:     event NetMessageHandler NetMessageReceived;

  85:  

  86:     /// <summary>

  87:     /// 消息发送完成之后触发。

  88:     /// </summary>

  89:     event MessageHandler MessageSend;

  90:  

  91:     /// <summary>

  92:     /// 接收消息完成之后触发。

  93:     /// </summary>

  94:     event MessageHandler MessageReceived;

  95: }

其中方法void AddHander<T>(IMessageHandler<T> hander) where T : IMessage实现对消息处理器的注册,以便收到IMessage之后选择合适的处理器进行处理。

四、消息和消息处理器

从以上的介绍我们可以明确的知道AgileEAS.NET SOA中间件Socket/Tcp框架是的一个基于消息对象的消息通信框架,那么其最核心的业务就是定义消息及消息的处理思路,我们称之为消息及消息处理器结构:

其中IMessage接口为Socket/Tcp框架中最重要的接口,所有高层的应用消息都需要实现本接口:

   1: /// <summary>

   2: /// 消息接口定义。

   3: /// </summary>

   4: /// <remarks>

   5: /// 这里所说的消息是指业务处理的最小单元,而不是传输于网络之间的网络消息。

   6: /// </remarks>

   7: public interface IMessage

   8: {

   9:     /// <summary>

  10:     /// 从指定的 MessageReader加载消息对象。

  11:     /// </summary>

  12:     /// <param name="reader">消息读取器。</param>

  13:     void Load(BufferReader reader);

  14:  

  15:     /// <summary>

  16:     /// 将消息对象保存到指定的MessageWriter。

  17:     /// </summary>

  18:     /// <param name="writer">消息编写器。</param>

  19:     void WriteTo(BufferWriter writer);

  20: }

其中Load和WriteTo实现IMessage消息对象实例与字节流之间进行相互转换,Load消息用于从字节流之中读取并实例化消息、WriteTo把消息转换为流写入消息流之中,在应用开发过程之中必须实现这两个方法并且在消息类上打上MessageAttribute标记:

   1: /// <summary>

   2: /// 消息ID属性。

   3: /// </summary>

   4: /// <remarks>

   5: /// 标记网络消息,确定其唯一的ID。

   6: /// </remarks>

   7: [AttributeUsage(AttributeTargets.Class)]

   8: public class MessageAttribute : Attribute

   9: {

  10:     /// <summary>

  11:     /// 初始化MessageAttribute对象实例。

  12:     /// </summary>

  13:     /// <param name="messageID">消息ID。</param>

  14:     public MessageAttribute(string messageID)

  15:         :this(messageID,string.Empty)

  16:     {

  17:  

  18:     }

  19:  

  20:     /// <summary>

  21:     /// 初始化MessageAttribute对象实例。

  22:     /// </summary>

  23:     /// <param name="messageID">消息ID。</param>

  24:     /// <param name="description">消息说明。</param>

  25:     public MessageAttribute(string messageID, string description)

  26:     {

  27:         this.MessageID = new Guid(messageID);

  28:         this.Description = description;

  29:     }

  30:  

  31:     /// <summary>

  32:     /// 消息ID。

  33:     /// </summary>

  34:     public Guid MessageID

  35:     {

  36:         get;

  37:         set;

  38:     }

  39:  

  40:     /// <summary>

  41:     /// 消息说明。

  42:     /// </summary>

  43:     public string Description

  44:     {

  45:         get;

  46:         set;

  47:     }

  48: }

MessageAttribute标记用于向已实现IMessage接口的具体消息的消息ID与消息说明,即向Socket通信框架声音本消息的唯一性之用,其中MessageID为一个GUID对象,GUID对象理论上是唯一的,我们可以表示消息的唯一性,以下是一个具体的消息例子:

   1: /// <summary>

   2: /// 用户登录消息。

   3: /// </summary>

   4: [Message("F42433DF-2D4D-4514-9523-2FE911E63CAA", "登录消息")]

   5: [Serializable]

   6: public class LoginMessage : IMessage

   7: {

   8:     /// <summary>

   9:     /// 用户名。

  10:     /// </summary>

  11:     public string LoginID

  12:     {

  13:         get;

  14:         set;

  15:     }

  16:  

  17:     /// <summary>

  18:     /// 密码。

  19:     /// </summary>

  20:     public string PassWord

  21:     {

  22:         get;

  23:         set;

  24:     }

  25:  

  26:     #region IMessage 成员

  27:  

  28:     /// <summary>

  29:     /// 

  30:     /// </summary>

  31:     /// <param name="reader"></param>

  32:     public void Load(EAS.IO.BufferReader reader)

  33:     {

  34:         LoginID = reader.ReadString();

  35:         PassWord = reader.ReadString();

  36:     }

  37:  

  38:     /// <summary>

  39:     /// 

  40:     /// </summary>

  41:     /// <param name="writer"></param>

  42:     public void WriteTo(EAS.IO.BufferWriter writer)

  43:     {

  44:         writer.Write(LoginID);

  45:         writer.Write(PassWord);

  46:     }

  47:  

  48:     #endregion

  49: }

以上是一个具体消息的例子,其表明消息ID为“F42433DF-2D4D-4514-9523-2FE911E63CAA”,其作用是登录消息,用于实现类似登录业务。

五、可靠的消息中间件

AgileEAS.NET SOA中间件Socket/Tcp框架是一个可靠的消息中间件,在设计过程之初就选择了完成端口模型进行开发,以保证服务的高并发和吞吐量,在底层消息通信上,我们选择了不超过8K的可变大小通信报文,比如当一个高层的IMeesage只有512字节内容的时候,会取转成一个一个远小于8K的报文进行发送,如果一个高层IMeesage为66K时,会被分解成为9条消息报文进行通信,前8条消息报文长度为8K,最后一条不满足8K,接收文收到这9条报文后组合并转换为IMeesage对象之后交由消息处理器IMeeesgaHandler进行处理。

在进行消息报文收发过程之中,经过长期测试、验证、设计和选择了高性能的防粘包设计,避免应用开发者头疼的消息粘包问题。

六、AgileEAS.NET SOA中间件需要的Socket/Tcp框架下载

AgileEAS.NET SOA中间件Socket/Tcp框架包含在AgileEAS.NET SOA中间件平台之中,具体定义在EAS.MicroKernel.dll程序集之中,要使用AgileEAS.NET SOA中间件Socket/Tcp框架进行基于Socket的通信开发,请通过AgilleEAS.NET SOA 中站件平台官方网站最新下载页面下载。

七、联系我们

为了完善、改进和推广AgileEAS.NET而成立了敏捷软件工程实验室,是一家研究、推广和发展新技术,并致力于提供具有自主知识产权的业务基础平台软件,以及基于业务基础平台了开发的管理软件的专业软件提供商。主要业务是为客户提供软件企业研发管理解决方案、企业管理软件开发,以及相关的技术支持,管理及技术咨询与培训业务。

AgileEAS.NET平台自2004年秋呱呱落地一来,我就一直在逐步完善和改进,也被应用于保险、医疗、电子商务、房地产、铁路、教育等多个应用,但一直都是以我个人在推广,2010年因为我辞职休息,我就想到把AgileEAS.NET推向市场,让更多的人使用。

我的技术团队成员都是合作多年的老朋友,因为这个平台是免费的,所以也没有什么收入,都是由程序员的那种理想与信念坚持,在此我感谢一起奋斗的朋友。

团队网站:http://www.agilelab.cn

AgileEAS.NET网站:http://www.agileeas.net

官方博客:http://eastjade.cnblogs.com

github:https://github.com/agilelab/eas

QQ:47920381

AgileEAS.NET QQ群:

113723486(AgileEAS SOA 平台)/上限1000人

199463175(AgileEAS SOA 交流)/上限1000人

120661978(AgileEAS.NET 平台交流)/上限1000人

212867943(AgileEAS.NET研究)/上限500人

147168308(AgileEAS.NET应用)/上限500人

172060626(深度AgileEAS.NET平台)/上限500人

116773358(AgileEAS.NET 平台)/上限500人

125643764(AgileEAS.NET探讨)/上限500人

193486983(AgileEAS.NET 平台)/上限500人

邮件:james@agilelab.cn,mail.james@qq.com,

电话:18629261335。

AgileEAS.NET SOA 中间件平台.Net Socket通信框架-介绍的更多相关文章

  1. AgileEAS.NET SOA 中间件平台.Net Socket通信框架-简单例子-实现简单的服务端客户端消息应答

    一.AgileEAS.NET SOA中间件Socket/Tcp框架介绍 在文章AgileEAS.NET SOA 中间件平台Socket/Tcp通信框架介绍一文之中我们对AgileEAS.NET SOA ...

  2. AgileEAS.NET SOA 中间件平台.Net Socket通信框架-完整应用例子-在线聊天室系统-下载配置

    一.AgileEAS.NET SOA中间件Socket/Tcp框架介绍 在文章AgileEAS.NET SOA 中间件平台Socket/Tcp通信框架介绍一文之中我们对AgileEAS.NET SOA ...

  3. AgileEAS.NET SOA中间件平台/敏捷软件开发平台

    AgileEAS.NET SOA中间件平台/敏捷软件开发平台 最新下载 一.前言 AgileEAS.NET SOA中间件平台,简称EAS.NET,是基于敏捷并行开发思想和Microsoft .Net构 ...

  4. AgileEAS.NET SOA 中间件平台 5.2 发布说明-包含Silverlight及报表系统的开源代码下载

    一.AgileEAS.NET SOA 中间件简介      AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速 ...

  5. AgileEAS.NET SOA中间件平台更新日志 2015-04-28

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  6. AgileEAS.NET SOA 中间件平台5.2版本下载、配置学习(二):配置WinClient分布式运行环境

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  7. AgileEAS.NET SOA 中间件平台5.2版本下载、配置学习(四):开源的Silverlight运行容器的编译、配置

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  8. AgileEAS.NET SOA 中间件平台5.2版本下载、配置学习(三):配置ActiveXForm运行环境

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  9. 重写AgileEAS.NET SOA 中间件平台账号密码的加密算法

    一.平台简介 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适 ...

随机推荐

  1. C#:泛型(Generic)

    前言:  此系列都为个人对C#的回顾,属于个人理解,新司机可参考.求老司机指点.如果有什么问题或不同见解,欢迎大家与我沟通! 目录:  泛型是什么 泛型的好处及用途 如何声明使用泛型 泛型类 泛型方法 ...

  2. C语言创建及解析Json的使用法则

    参考原文:http://blog.csdn.net/xukai871105/article/details/33013455 JSON(JavaScriptObject Notation)是一种轻量级 ...

  3. 关于C中内存操作

     from:http://blog.csdn.net/shuaishuai80/article/details/6140979 malloc.calloc.realloc的区别 C Language ...

  4. PHP中Exception异常

    异常的基本使用 当异常被抛出时,其后的代码不会继续执行,PHP 会尝试查找匹配的 "catch" 代码块. 如果异常没有被捕获,而且又没用使用 set_exception_hand ...

  5. 2.0、Hibernate框架的简单搭建

    一.Hibernate:是一个开放源代码的对象关系映射框架,对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句 ...

  6. C#设置输入框只输入数字

    为输入框添加keyPress事件,然后添加代码: || e.KeyChar > ) && e.KeyChar != && e.KeyChar != &&a ...

  7. Java 多线程编程

    1.synchronized/wait/notify package javamultithread; import java.util.logging.Level; import java.util ...

  8. winform 跨线程操作控件

    当进行winform的开发时,经常遇到用时比较久的操作,在传统的单线程程序中,用户必须等待这个耗时操作完成以后才能进行下一步的操作,这个时候,多线程编程就派上用场了,将这个耗时的操作放到一个新的子线程 ...

  9. 适配ios9出现的问题:-canOpenURL: failed for URL

    -canOpenURL: failed for URL: "wtloginmqq2://qzapp" - error: "(null)" 2015-09-13 ...

  10. mac gem install nokogiri error

    Gem::Ext::BuildError: ERROR: Failed to build gem native extension. /Users/angela/.rbenv/versions/1.9 ...