• 使用SuperSocket实现TLV自定义协议网络通信的Demo


          前些天看到江大鱼发布了SuperSocket 1.4正式版,由于最近项目使用到了网口的通讯编程,于是,也查看了下SuperSocket源代码,发现架构还是很清晰的,并且易于扩展,具体中文文档详见:http://www.cnblogs.com/jzywh/archive/2011/04/19/SuperSocketDocument.html

          今天我要说的是,如果通过SuperSocket自定义一个应用协议。由于项目中的服务通过TLV的协议进行数据传输,而通过网口是其中的一个通讯方式,当然项目中还包括通过串口、USB设备、USB数据线等等进行数据传输。其中网口通讯,采用TCP通讯的方式。什么是TLV呢,可以参考一下我之前写的一篇文章:《金融系统中PBOC/EMV的TLV的算法实现》,里面对于TLV有了详细的介绍,这里不再敖述。由于项目中的TLV协议还要涉及到一些报文头以及校验码的考虑,本篇文章将跳过这些考虑,直接谈核心的数据包TLV的传输。

    具体思路

    1、创建一个解决方案TLVSocketDemo。

    2、创建项目TLVSocketDemo.Server,这个项目主要作为服务端的基本类库,包含一些AppServer,AppSession的扩展实现。当然SuperSocket 提供了一个名叫"SocketService"的项目,它是一个能够让AppServer运行于其中的容器。于是需要把SocketService相关的一些文件复制到生成输出目录中。

    image

    3、AppServer的子类实现:

    public class TLVProtocolServer : AppServer 

        
    public TLVProtocolServer() 
            : 
    base(new TLVProtocol()) 
        {

        } 
    }

    4、AppSession的子类实现:

    public class TLVProtocolSession : AppSession 

        
    public override void HandleExceptionalError(Exception e) 
        {

        } 
    }

    5、TLVEntityListCommandInfo是服务器端收到的一个命令实体类,这个类必须继承于CommandInfo:

    public class TLVEntityListCommandInfo : CommandInfo> 

        
    public TLVEntityListCommandInfo(string key, List data) 
            : 
    base(key, data) 
        {

        } 
    }

    实际上,TLVEntity就是一个TLV的实体结构。TLVEntity类的实现:

        [Serializable] 
        
    ///  
        
    /// TLV实体 
        
    ///  
        public class TLVEntity 
        { 
            
    ///  
            
    /// 标记 
            
    ///  
            public byte[] Tag { getset; }

            
    ///  
            
    /// 数据长度 
            
    ///  
            public byte[] Length { getset; }

            
    ///  
            
    /// 数据 
            
    ///  
            public byte[] Value { getset; }

            
    ///  
            
    /// 标记占用字节数 
            
    ///  
            public int TagSize { getset; }

            
    ///  
            
    /// 数据长度占用字节数 
            
    ///  
            public int LengthSize { getset; }

            
    ///  
            
    /// 子嵌套TLV实体 
            
    ///  
            public TLVEntity Sub_TLVEntity { getset; } 
        }

    6、自定义协议:TLVProtocol.cs,继承于ICustomProtocol的范型接口:

    public class TLVProtocol : ICustomProtocol 

        
    public ICommandReader CreateCommandReader(IAppServer appServer) 
        { 
            
    return new TLVCommandReader(appServer); 
        } 
    }

    通过创建(CreateCommandReader方法)来得到字节数组数据的的读取方式。

    而TLVCommandReader主要就是作为进行命令解析。

    public class TLVCommandReader : CommandReaderBase 
        { 
            
    public TLVCommandReader(IAppServer appServer) 
                : 
    base(appServer) 
            {

            }

            
    public override TLVEntityListCommandInfo FindCommandInfo(IAppSession session, byte[] readBuffer, int offset, int length, bool isReusableBuffer, out int left) 
            { 
                left 
    = 0;

                AddArraySegment(readBuffer, offset, length, isReusableBuffer);

                
    byte[] source = BufferSegments.ToArrayData();

                List list 
    = TLVPackage.Construct(source);

                
    return new TLVEntityListCommandInfo("ECHO", list); 
            } 
        } 
    }

    注意到FindCommandInfo方法可以对字节数组数据进行读取以及构造TLV实体。

    TLVPackage.Construct的实现在上一篇文章有提到。

    7、编写命令类:

    public class ECHO : CommandBase 

        
    public override void ExecuteCommand(TLVProtocolSession session, TLVEntityListCommandInfo commandInfo) 
        { 
            
    string data = BinaryUtil.BinarySerialize(commandInfo.Data); 
            session.SendResponse(data); 
        } 
    }

    这里发送给客户端的数据,会先将List<Entity>列表通过序列化,并且再进行Base64编码。通过SendResponse发送到客户端。

    8、修改SuperSocket.SocketService.exe.configTLV服务的相关配置。

    9、最后将项目生成后,项目的路径输出到SuperSocket.SocketServer.exe对应目录中。至此编译通过以后,TLV服务的服务端已经写好了。是不是很容易呢。

    10、接着创建一个TLVSocketDemo.Client的客户端程序:

    static int port = 911
    static void Main(string[] args) 

        EndPoint serverAddress 
    = new IPEndPoint(IPAddress.Parse("127.0.0.1"), port);

        
    using (Socket socket = new Socket(serverAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) 
        { 
            socket.Connect(serverAddress);

            
    byte[] data = new byte[0]; 
            
    using (FileStream stream = new FileStream(Application.StartupPath + "\\tlvdata.dat", FileMode.Open, FileAccess.Read)) 
            { 
                
    using (BinaryReader reader = new BinaryReader(stream)) 
                { 
                    reader.BaseStream.Seek(
    0, SeekOrigin.Begin); 
                    data 
    = reader.ReadBytes((int)reader.BaseStream.Length); 
                } 
            }

            socket.Send(data, data.Length, SocketFlags.None);

            
    byte[] temp = new byte[5000];

            
    int read = socket.Receive(temp, 05000, SocketFlags.None);

            
    byte[] response = new byte[read]; 
            Array.Copy(temp, 
    0, response, 0, read);

            
    string responseText = Encoding.ASCII.GetString(response);

            List list 
    = BinaryUtil.BinaryDeserialize>(responseText);

            Console.WriteLine(
    "服务端已经构造{0}个TLV结构.", list.Count);

            Console.ReadKey(); 
        } 
    }

    这里说明有一下,通过tlvdata.dat文件读取字节数组数据,然后发送数据到服务端,服务端处理完以后,回发给客户端,返回的是一个Base64编码。然后编译Client的代码,通过完成。

    11、最后运行程序,首先运行SuperSocket.SocketService的服务,直接可以点开RunServer.bat,或者也可以通过InstallService.bat进行Windows服务的安装;接着运行客户端程序TLVSocketDemo.Client:

    Server端:

    image

    Client端:

    image

    说明服务端对TLV字节数组数据已经构造了2个TLV结构。通过ExecuteCommand的参数可以得到CommandInfo的Data数据,它就是List<TLVEntity>。

    总结:SuperSocket的自定义协议的扩展还是很容易使用的,大家也可以通过该扩展方式自定义你自己的应用协议。

    附上Server和Client的Demo源代码:TLVSocketDemo.rar

  • 相关阅读:
    【FIRST USE】第一次用git把代码上传到github
    [多线程学习笔记] 一个线程安全的队列
    C中宏展开问题
    C语言中的命名空间
    c语言中赋值语句的结果
    Python一些难以察觉的错误
    C语言中的内存压缩技术
    简述数据结构:二叉查找树
    指针与指针的引用、指针的指针
    C++ 中的virtual关键词
  • 原文地址:https://www.cnblogs.com/liping13599168/p/2101064.html
Copyright © 2020-2023  润新知