轻量级的通信引擎 StriveEngine

如果ESFramework对您的项目来说,太庞大、太重量级;如果您的项目不需要P2P、不需要传文件、不需要群集等功能,只需要简单的TCP/UDP通信。那么,可以考虑使用轻量级的通信引擎StriveEngine。相比较而言,StriveEngine更单纯、更容易上手,也更容易与已存在的遗留系统进行协作。 

一.StriveEngine 主要特性

1.底层采用高效的IOCP(完成端口)模型。

2.内部自动管理可复用的线程池、以及内存池。

3.内置多种通信引擎类型:TCP/UDP、文本协议/二进制协议、服务端/客户端。而且,所有这些引擎的使用方式一致。

4.解决了TCP通信中的粘包以及消息重组问题。

5.发送消息支持同步、异步两种方式。

6.服务端引擎支持异步消息队列模式。

7.客户端TCP引擎支持断线自动重连。

8.支持Sock5代理。

9.兼容IPv6。 

 

二.StriveEngine与ESFramework/ESPlus/ESPlatform的区别

1.ESFramework 是一个功能强大的通信框架/平台,而StriveEngine是一个单纯高效的通信引擎类库。

2.ESFramework 更贴近应用层,而StriveEngine更贴近Socket层。

3.ESFramework 使用UserID标志每一个在线的客户端,而StriveEngine则使用低阶的IPEndPoint来标志每一个在线的客户端。

4.可以认为StriveEngine就是ESFramework底层使用的内核。

5.ESFramework 定义了底层通信消息的格式(对ESFramework使用者是透明的),而StriveEngine对通信消息的格式未作任何定义。

6.ESFramework和StriveEngine都提供了服务端引擎和客户端引擎。当涉及到要开发新的其它平台引擎来与ESFramework或StriveEngine协作时,比如:

(1)如果要开发其它平台的客户端引擎与ESFramework的服务端引擎协作,则必需实现ESPlus协议格式;

(2)如果要开发其它平台的客户端引擎与StriveEngine的服务端引擎协作,由于StriveEngine未定义任何消息格式,所以不存在要必需实现既有协议格式的问题。 

(3)与(2)同理,使用 StriveEngine的客户端引擎,可以与任何其它现有的服务端协作。 

7.对于那些涉及到在线用户管理、以UserID为中心的应用(比如即时通信应用),或者需要P2P通信、传文件等功能的应用来说,ESFramework更适合; 

  对于那些仅仅需要简单而高效的通信功能的应用(如数据采集、消息转发等)来说,StriveEngine更合适。  

 

三.StriveEngine 整体结构

StriveEngine内置了多种通信引擎,这些通信引擎都以接口的方式暴露了其功能,而不同的引擎却都依据某些共通的部分,继承了相同的基接口。  引擎接口的继承关系如下所示:

  

INetworkEngine 是所有引擎的根接口,只要是StriveEngine中的内置引擎,都实现了这个接口。

IServerEngine 是服务端引擎接口。IPassiveEngine 是客户端引擎接口。

ITcpServerEngine 是基于TCP的服务端引擎接口。ITcpPassiveEngine是基于TCP的客户端引擎接口。

IUdpEngine 是基于UDP的通信引擎接口。由于UDP是非连接协议,可以认为UDP通信的双方是对等的,所以,服务端和客户端都使用IUdpEngine即可。

ITextEngine 是基于文本协议的通信引擎的基接口。

下面,我们来详细看看每个接口。

1.INetworkEngine

 

/// <summary>
    /// 所有网络引擎(包括服务端引擎、客户端引擎、TCP引擎、UDP引擎、二进制引擎、文本引擎)的基础接口。    
    /// </summary>
    public interface INetworkEngine : IDisposable
    {      
        /// <summary>            
        /// 传输层协议类型,TCP或UDP
        /// </summary>
        ProtocolType ProtocolType { get; }        

        /// <summary>
        /// 引擎实例的创建时间。
        /// </summary>
        DateTime CreateTime { get; }

        /// <summary>
        /// 表示要监听本地的哪个端口。如果设定其值小于等于0,则表示由系统自动分配。      
        /// 注意,引擎初始化完成后,不能再修改该属性。
        /// </summary>
        int Port { get; set; }

        /// <summary>
        /// 对于服务端引擎,表示要绑定本地的哪个IP,如果设为null,则表示绑定本地所有IP。
        /// 对于客户端引擎,表示要绑定本地的哪个IP与服务器进行通信。如果设为null(其值会在初始化完成后被修改),则自动选择地址列表中的某一个。
        /// 注意,引擎初始化完成后,不能再修改该属性。
        /// </summary>
        string IPAddress { get; set; }

        /// <summary>
        /// 设置日志记录的文件路径。如果设为null,表示不记录日志。默认值为null。
        /// </summary>
        string LogFilePath { set; }

        /// <summary>
        /// Socket(网卡)发送缓冲区的大小。默认为8k。
        /// </summary>
        int SocketSendBuffSize { get; set; }

        /// <summary>
        /// Socket(网卡)接收缓冲区的大小。默认为8k。
        /// </summary>
        int SocketReceiveBuffSize { get; set; }

        /// <summary>
        /// 网络引擎能够接收的最大的消息尺寸。据此网络引擎可以为每个Session/Connection开辟适当大小的接收缓冲区。
        /// 默认为1k。当接收到的消息尺寸超过MaxMessageSize时,将会关闭对应的连接(对TCP)或丢弃数据(对UDP)。
        /// </summary>
        int MaxMessageSize { get; set; }    

        /// <summary>
        /// 引擎实例是否已经被释放。
        /// </summary>
        bool Disposed { get; }

        /// <summary>
        /// 初始化并启动网络引擎。如果修改了引擎配置参数,在应用新参数之前必须先重新调用该方法初始化引擎。
        /// </summary>
        void Initialize();      

        /// <summary>
        /// 当不再使用当前引擎实例时,释放它。(异步方式)
        /// 注意:对于UDP或客户端引擎,如果消息是同步处理的(HandleMessageAsynchronismly为false),请不要在消息处理器中调用Dispose方法,否则,会导致死锁。因为停止引擎要等到最后一条消息处理完毕才会返回。
        /// 可以转向调用异步的DisposeAsyn方法。
        /// </summary>
        void DisposeAsyn();
        
        /// <summary>
        /// 当引擎实例被释放时,触发此事件。
        /// </summary>
        event CbDelegate<INetworkEngine> EngineDisposed;

        /// <summary>
        /// 接收到一个完整的消息时触发该事件。
        /// </summary>
        event CbDelegate<IPEndPoint,byte[]> MessageReceived;

        /// <summary>
        /// 将消息成功发送之后触发该事件
        /// </summary>
        event CbDelegate<IPEndPoint, byte[]> MessageSent;       
    }

    /// <summary>
    /// 传输层协议类型。
    /// </summary>
    public enum ProtocolType
    {
        TCP = 0,
        UDP
    }

    /// <summary>
    /// 协议的格式。
    /// </summary>
    public enum ContractFormatStyle
    {
        /// <summary>
        /// 流协议或称二进制协议。
        /// </summary>
        Stream = 0,
        /// <summary>
        /// 文本协议,如基于xml的。
        /// </summary>
        Text
    }    

(1)StriveEngine中的所有通信引擎在设置完必要的属性后,都必须调用Initialize方法进行初始化,初始化完成后,引擎实例才开始正常工作。

(2)INetworkEngine从IDisposable继承,表明通信引擎内部持有了重要的资源,当不再使用其实例时,要尽快调用IDisposable的Dispose方法释放资源。

(3)当通信引擎被释放后,会触发EngineDisposed事件,并且此后,Disposed属性将返回true。

(4)请根据应用的需要谨慎地设置MaxMessageSize,如果设置的过大,可能会造成内存空间的浪费(特别是对于基于文本协议的服务端引擎)。

(5)通过MessageReceived事件,可以得到通信引擎接收到的所有消息;通过MessageSent事件,可以监控通信引擎发送出去的所有消息。 

2.IServerEngine

    /// <summary>
    /// 服务端引擎接口。
    /// </summary>
    public interface IServerEngine : INetworkEngine
    {
        /// <summary>
        /// 到目标客户的通道是否繁忙?
        /// </summary>
        bool ChannelIsBusy(IPEndPoint client);
 
        /// <summary>
        /// 给某个客户端同步发信息。注意:如果引擎已经停止或客户端不在线,则直接返回。   
        /// </summary>
        /// <param name="client">接收数据的客户端</param>
        /// <param name="msg">消息</param>
        void SendMessageToClient(IPEndPoint client, byte[] msg);
 
        /// <summary>
        /// 给某个客户端同步发信息。注意:如果引擎已经停止或客户端不在线,则直接返回。   
        /// </summary>
        /// <param name="client">接收数据的客户端</param>
        /// <param name="msg">消息</param>
        /// <param name="action">当通道繁忙时采取的动作:继续发送或丢弃。</param>        
        void SendMessageToClient(IPEndPoint client, byte[] msg, ActionTypeOnChannelIsBusy action);
 
        /// <summary>
        /// 给某个客户端异步发信息。注意:如果引擎已经停止或客户端不在线,则直接返回。   
        /// </summary>
        /// <param name="client">接收数据的客户端</param>
        /// <param name="msg">消息</param>    
        void PostMessageToClient(IPEndPoint client, byte[] msg);
 
        /// <summary>
        /// 给某个客户端异步发信息。注意:如果引擎已经停止或客户端不在线,则直接返回。   
        /// </summary>
        /// <param name="client">接收数据的客户端</param>
        /// <param name="msg">消息</param>
        /// <param name="action">当通道繁忙时采取的动作:继续发送或丢弃。</param>        
        void PostMessageToClient(IPEndPoint client, byte[] msg, ActionTypeOnChannelIsBusy action);
    }  

(1)SendMessageToClient和PostMessageToClient 分别表示同步和异步发送消息给客户端。

(2)ChannelIsBusy 指的是在与目标客户端的TCP连接上,是否有数据正在发送(服务端至客户端)。

(3)ActionTypeOnChannelIsBusy 参数允许我们在通道繁忙时,丢弃不重要的消息。 

3.IPassiveEngine 

    /// <summary>
    /// 客户端引擎接口。
    /// </summary>
    public interface IPassiveEngine :INetworkEngine
    {
        /// <summary>
        /// 服务器地址。
        /// </summary>
        AgileIPE ServerIPEndPoint { get; set; }   

        /// <summary>
        /// 发送消息的通道是否正忙。使用者可以根据该属性决定是否要丢弃后续某些消息的发送。
        /// </summary>
        bool ChannelIsBusy { get; }     

        /// <summary>
        /// 将消息异步发送给服务器,不经任何处理,直接发送。注意:如果引擎已经停止,则直接返回。   
        /// </summary>
        /// <param name="msg">要发送的消息</param>
        /// <param name="action">当通道繁忙时采取的动作:继续发送或丢弃。</param>
        void SendMessageToServer(byte[] msg, ActionTypeOnChannelIsBusy action);

        /// <summary>
        /// 将消息异步发送给服务器,不经任何处理,直接发送。注意:如果引擎已经停止,则直接返回。   
        /// </summary>
        /// <param name="msg">要发送的消息</param>       
        void SendMessageToServer(byte[] msg);

        /// <summary>
        /// 将消息同步发送给服务器,不经任何处理,直接发送。注意:如果引擎已经停止,则直接返回。   
        /// </summary>
        /// <param name="msg">要发送的消息</param>
        /// <param name="action">当通道繁忙时采取的动作:继续发送或丢弃。</param>
        void PostMessageToServer(byte[] msg, ActionTypeOnChannelIsBusy action);

        /// <summary>
        /// 将消息同步发送给服务器,不经任何处理,直接发送。注意:如果引擎已经停止,则直接返回。   
        /// </summary>
        /// <param name="msg">要发送的消息</param>       
        void PostMessageToServer(byte[] msg);
    }    

(1)SendMessageToServer和PostMessageToServer 分别表示同步和异步发送消息给服务端。

(2)ChannelIsBusy 指的是当前与服务器的TCP连接上,是否有数据正在发送(客户端至服务端)。

(3)ActionTypeOnChannelIsBusy 参数允许我们在通道繁忙时,丢弃不重要的消息。  

4.ITcpServerEngine 

    /// <summary>
    /// TCP服务端引擎接口。
    /// </summary>
    public interface ITcpServerEngine : IServerEngine
    {
        /// <summary>
        /// 引擎所采用的协议类型(二进制、文本)。
        /// </summary>
        ContractFormatStyle ContractFormatStyle { get; }

        /// <summary>
        /// 当前在线客户端的数量。
        /// </summary>
        int ClientCount { get; }  
      
        /// <summary>
        /// 服务器允许最大的同时在线客户端数。
        /// </summary>
        int MaxClientCount { get;}       

        /// <summary>
        /// 给每个连接发送数据超时时间(单位:毫秒。默认为-1,表示无限)。如果在指定的时间内未将数据发送完,则关闭对应的连接。       /// </summary>
        int WriteTimeoutInMSecs { get; set; }/// <summary>
        /// 每个通道连接上允许最大的等待发送【包括投递】以及正在发送的消息个数。
        /// 当等待发送以及正在发送的消息个数超过该值时,将关闭对应的连接。如果设置为0,则表示不作限制。默认值为0。       /// </summary>
        int MaxChannelCacheSize { get; set; }     

        /// <summary>
        /// 监听器是否开启。
        /// </summary>
        bool IsListening { get; }

        /// <summary>
        /// 关闭到某个客户端的连接,将触发SomeOneDisconnected事件。
        /// </summary>     
        void CloseClient(IPEndPoint client);

        /// <summary>
        /// 关闭所有客户端的连接。
        /// </summary>
        void CloseAllClient();
       
        /// <summary>
        /// 关闭或开启监听器。该方法调用不影响网络引擎的消息接收和处理。
        /// </summary>      
        void ChangeListenerState(bool enabled);       
       
        /// <summary>
        /// 获取所有在线连接的客户端的地址。
        /// </summary>        
        List<IPEndPoint> GetClientList();

        /// <summary>
        /// 客户端是否在线?
         /// </summary>        
        bool IsClientOnline(IPEndPoint client);

        /// <summary>
        /// 当某TCP连接断开时,触发该事件
        /// </summary>
        event CbDelegate<IPEndPoint> ClientDisconnected;

        /// <summary>
        /// 当某TCP连接建立成功时,触发此事件。
        /// </summary>
        event CbDelegate<IPEndPoint> ClientConnected;

        /// <summary>
        /// 当tcp连接数量发生变化时,触发此事件。
        /// </summary>
        event CbDelegate<int> ClientCountChanged;      

        /// <summary>
        /// 当连接监听器的状态发生变化时,触发此事件。事件参数为true,表明连接监听器启动;事件参数为false,表明连接监听器已停止。
        /// </summary>
        event CbDelegate<bool> ListenerStateChanged;
    }

(1)WriteTimeoutInMSecs 用于设置发送数据的超时。 最好给该属性赋一个适当的值,因为在某些情况下,发送数据可能会导致很长时间的阻塞。该属性只对同步发送有效。

(2)MaxChannelCacheSize 是服务端的一个安全设置。该设置用于防止服务器为速度慢的通道缓存太多的消息,而导致服务器内存无限制增长。

(3)ChangeListenerState 用于改变服务器的监听状态,其将触发ListenerStateChanged事件,并改变IsListening属性的值。

        如果IsListening为false,表示当前不接受新的TCP连接请求。 

(4)当有连接建立或断开时,将分别触发ClientConnected和ClientDisconnected事件。 

5.ITcpPassiveEngine

    /// <summary>
    /// 客户端TCP引擎接口。 
    /// </summary>
    public interface ITcpPassiveEngine :IPassiveEngine
    {
        /// <summary>
        /// 引擎所采用的协议类型(二进制、文本)。
        /// </summary>
        ContractFormatStyle ContractFormatStyle { get; }

        /// <summary>
        /// 当客户端与服务器的TCP连接断开时,将触发此事件。
         /// </summary>
        event CbDelegate ConnectionInterrupted;

        /// <summary>
        /// 自动重连开始时,触发此事件。
        /// </summary>
        event CbDelegate ConnectionRebuildStart; 

        /// <summary>
        /// 自动重连成功后,触发此事件。
        /// </summary>
        event CbDelegate ConnectionRebuildSucceed; 

        /// <summary>
        /// 自动重连超过最大重试次数时,表明重连失败,将触发此事件。
        /// </summary>
        event CbDelegate ConnectionRebuildFailure;

        /// <summary>
        /// Sock5代理服务器信息。如果不需要代理,则设置为null。
         /// </summary>
        Sock5ProxyInfo Sock5ProxyInfo { get; set; }       

        /// <summary>
        /// 当前是否处于连接状态。
        /// </summary>
        bool Connected { get; }

        /// <summary>
        /// 当与服务器断开连接时,是否自动重连。
         /// </summary>
        bool AutoReconnect { get; set; }

        /// <summary>
        /// 当连接断开时,自动重连尝试的最大次数。默认值为int.MaxValue。
        /// </summary>
        int MaxRetryCount4AutoReconnect { get; set; }         

        /// <summary>
        /// 主动关闭与服务器的连接。如果AutoReconnect为true,将引发自动重连。
         /// </summary>
        void CloseConnection();

        /// <summary>
        /// 手动重连。如果当前处于连接状态,则直接返回。
        /// </summary>
        /// <param name="retryCount">重试次数</param>
        /// <param name="retrySpanInMSecs">重试间隔时间,毫秒</param>
        void Reconnect(int retryCount, int retrySpanInMSecs);
    }    

(1)如果AutoReconnect设置为true,表示启用自动重连,那么,当连接断开时,会按以下顺序触发相关事件: ConnectionInterrupted 、ConnectionRebuildStart 、   ConnectionRebuildSucceed/ConnectionRebuildFailure。

(2)注意,如果AutoReconnect设置为true,CloseConnection将会先关闭当前连接,然后再启动自动重连。 

6.IUdpEngine 

    /// <summary>
    /// Udp引擎基础接口。
    /// </summary>
    public interface IUdpEngine  :INetworkEngine
    {       
        /// <summary>
        /// 向指定的端点发送UDP消息。注意:如果引擎已经停止,则直接返回。
        /// </summary>
        /// <param name="message">要发送的消息</param>
        /// <param name="address">目标端点</param>           
        void SendMessage(IPEndPoint address, byte[] message);

        /// <summary>
        /// 向指定的端点投递UDP消息。注意:如果引擎已经停止,则直接返回。
        /// </summary>
        /// <param name="message">要投递的消息</param>
        /// <param name="address">目标端点</param>           
        void PostMessage(IPEndPoint address, byte[] message);
    }

UDP是非连接的协议,所以,UDP引擎不用区分客户端和服务端,或者说,无论是客户端还是服务端,都可以使用IUdpEngine。注意:IUdpEngine也从INetworkEngine继承,所以,它具备了StriveEngine中基础引擎所有的功能。 

 

四.如何使用StriveEngine

在StriveEngine中,我们不能直接new某个通信引擎的class来获得其实例。StriveEngine提供了NetworkEngineFactory,我们可以通过工厂的静态方法来得到通信引擎实例的引用。

1.通信引擎工厂

    /// <summary>
    /// 通信引擎工厂。
    /// </summary>
    public static class NetworkEngineFactory
    {        
        /// <summary>
        /// 创建使用二进制协议的TCP服务端引擎。对于返回的引擎实例,可以设置其更多属性,然后调用其Initialize方法启动引擎。
        /// </summary>
        /// <param name="port">服务端引擎监听的端口号</param>
        /// <param name="helper">二进制协议助手接口</param>        
        public static ITcpServerEngine CreateStreamTcpServerEngine(int port, IStreamContractHelper helper);        
        
        /// <summary>
        /// 创建使用文本协议的TCP服务端引擎。对于返回的引擎实例,可以设置其更多属性,然后调用其Initialize方法启动引擎。
        /// 注意:返回的引擎实例,可以强转为ITextEngine接口。
        /// </summary>
        /// <param name="port">服务端引擎监听的端口号</param>
        /// <param name="helper">文本协议助手接口</param>  
        public static ITcpServerEngine CreateTextTcpServerEngine(int port, ITextContractHelper helper);       
       
        /// <summary>
        /// 创建使用二进制协议的TCP客户端引擎。对于返回的引擎实例,可以设置其更多属性,然后调用其Initialize方法启动引擎。
        /// </summary>        
        /// <param name="serverIP">要连接的服务器的IP</param> 
        /// <param name="serverPort">要连接的服务器的端口</param> 
        /// <param name="helper">二进制协议助手接口</param>        
        public static ITcpPassiveEngine CreateStreamTcpPassivEngine(string serverIP, int serverPort, IStreamContractHelper helper);    
               
        /// <summary>
        /// 创建使用文本协议的TCP客户端引擎。对于返回的引擎实例,可以设置其更多属性,然后调用其Initialize方法启动引擎。
        /// 注意:返回的引擎实例,可以强转为ITextEngine接口。
        /// </summary>
        /// <param name="serverIP">要连接的服务器的IP</param> 
        /// <param name="serverPort">要连接的服务器的端口</param> 
        /// <param name="helper">文本协议助手接口</param>  
        public static ITcpPassiveEngine CreateTextTcpPassiveEngine(string serverIP, int serverPort, ITextContractHelper helper);
        
        /// <summary>
        /// 创建UDP引擎。对于返回的引擎实例,可以设置其更多属性,然后调用其Initialize方法启动引擎。
        /// </summary>       
        public static IUdpEngine CreateUdpEngine();
    }

我们可以根据项目的需要(是TCP还是UDP、是文本协议还是二进制协议、是用于客户端还是用于服务端),来调用NetworkEngineFactory的对应方法获得正确的通信引擎实例。

注意:NetworkEngineFactory创建的所有通信引擎实例,必须要调用其Initialize方法后,引擎才算正常启动。当然,在调用Initialize之前,可以根据需要设置其相关的属性。

2.ContractHelper

StriveEngine内部通过ContractHelper来从接收的网络流中识别完整的消息,针对消息格式为文本和二进制,ContractHelper就划分为对应的ITextContractHelper 和IStreamContractHelper。我们看到,在通过NetworkEngineFactory创建通信引擎实例时,其有个参数是必须传入ContractHelper引用的。所以,在项目中,我们必须实现ITextContractHelper或者是IStreamContractHelper。

(1) ITextContractHelper  

    /// <summary>
    /// 文本协议助手接口。
    /// </summary>
    public interface ITextContractHelper 
    {
        /// <summary>
        /// 消息的结束标识符(经过编码后得到的字节数组)。
        /// </summary>
        byte[] EndToken { get; }     
    }

当使用文本协议时,通常,使用某一个或多个特殊的字符作为消息的结束符(EndToken)。ITextContractHelper的EndToken是一个byte[],其指的是消息的结束符经过编码(比如UTF-8)后得到的字节数组。通信引擎即通过ITextContractHelper提供的EndToken来从网络流中识别完整的消息。 

如果是使用UTF-8对文本消息(当然,也包括消息结束符)进行编解码,则可以使用StriveEngine内置的DefaultTextContractHelper,其实,它的实现非常简单: 

    /// <summary>
    /// StriveEngine内置的ITextContractHelper实现。使用UTF-8对EndToken进行编码。
    /// </summary>
    public class DefaultTextContractHelper : ITextContractHelper
    {        
        public DefaultTextContractHelper(string endTokenStr)
        {
            this.endToken = System.Text.Encoding.UTF8.GetBytes(endTokenStr);
        }

        private byte[] endToken;
        public byte[] EndToken
        {
            get
            {
                return this.endToken;
            }
        }
    }

(2)IStreamContractHelper  

    /// <summary>
    /// 二进制协议助手接口。
    /// </summary>
    public interface IStreamContractHelper
    {
        /// <summary>
        /// 从消息头中解析出消息体的长度。
        /// </summary>
        /// <param name="head">完整的消息头,长度固定为MessageHeaderLength</param>
        /// <returns>消息体的长度</returns>
        int ParseMessageBodyLength(byte[] head);

        /// <summary>
        /// 消息头的长度。
        /// </summary>
        int MessageHeaderLength { get; }
    }

当使用二进制协议时,通常,消息分为消息头(Header)和消息体(Body)两部分,消息头是必须的,而消息体可以为null。消息头的长度是固定的(比如8个字节),且其至少包含了一个字段--消息体的长度(或根据消息头的内容可以间接结算出消息体的长度)。

3.使用StriveEngine的步骤

(1)实现ITextContractHelper或者是IStreamContractHelper接口。

(2)调用NetworkEngineFactory的创建引擎的方法,得到正确的通信引擎实例。

(3)根据需要,设置引擎实例的某些属性(如MaxMessageSize属性)。

(4)根据需要,预定引擎实例的某些事件(如MessageReceived事件)。

(5)调用引擎实例的Initialize方法启动通信引擎。 

 

五.Demo下载

通过上述的内容大致了解了StriveEngine后,接下来我们通过一个简单的demo来看看在实际项目中是如何使用StriveEngine的。

该Demo是一个简单的客户端与服务端进行消息通信的demo,消息使用文本协议。该Demo总共包括三个项目:

(1)StriveEngine.SimpleDemoServer:基于StriveEngine开发的服务端。

(2)StriveEngine.SimpleDemoClient:基于StriveEngine开发的客户端。

(3)StriveEngine.SimpleDemo:直接基于.NET的Socket开发的客户端,其目的是为了演示:在客户端不使用StriveEngine的情况下,如何与基于StriveEngine的服务端进行通信。

Demo运行起来后的截图如下所示:

      

    Demo源码下载

更多信息请参见:www.oraycn.com


原文地址:https://www.cnblogs.com/dyllove98/p/3127587.html