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

原文地址:http://www.cnblogs.com/liping13599168/archive/2011/07/09/2101064.html 


前些天看到江大鱼发布了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 {  get set ; }

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

        
///   
        
///  数据 
        
///   
         public   byte [] Value {  get set ; }

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

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

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

 

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,  0 5000 , 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TLV协议是一种常用的数据编码格式,它由Tag(标签)、Length(长度)和Value(值)三部分组成。在C语言中,可以使用结构体来实现TLV协议。 首先,定义一个TLV结构体,包含Tag、Length和Value三个成员: ```c typedef struct tlv { uint8_t tag; uint16_t length; uint8_t *value; } TLV; ``` 其中,Tag用一个字节表示,Length用两个字节表示,Value是一个指向字节数组的指针。 接下来,可以定义一些函数来进行TLV编码和解码: ```c // 编码一个TLV结构体 void encode_tlv(TLV *tlv, uint8_t *buf, int *len) { buf[0] = tlv->tag; buf[1] = (tlv->length >> 8) & 0xFF; buf[2] = tlv->length & 0xFF; memcpy(buf + 3, tlv->value, tlv->length); *len = 3 + tlv->length; } // 解码一个TLV结构体 void decode_tlv(TLV *tlv, uint8_t *buf, int len) { tlv->tag = buf[0]; tlv->length = (buf[1] << 8) | buf[2]; tlv->value = malloc(tlv->length); memcpy(tlv->value, buf + 3, tlv->length); } ``` 编码函数将一个TLV结构体编码成一个字节数组,解码函数将一个字节数组解码成一个TLV结构体,并将Value成员指向新分配的内存空间。 对于TLV序列,可以定义一个TLV链表结构体来存储多个TLV结构体: ```c typedef struct tlv_list { TLV *tlv; struct tlv_list *next; } TLV_LIST; ``` 其中,tlv指向一个TLV结构体,next指向下一个TLV链表结构体。 可以通过遍历TLV链表来实现多个TLV结构体的编码和解码。具体实现方法可以参考以下示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> typedef struct tlv { uint8_t tag; uint16_t length; uint8_t *value; } TLV; typedef struct tlv_list { TLV *tlv; struct tlv_list *next; } TLV_LIST; // 编码一个TLV结构体 void encode_tlv(TLV *tlv, uint8_t *buf, int *len) { buf[0] = tlv->tag; buf[1] = (tlv->length >> 8) & 0xFF; buf[2] = tlv->length & 0xFF; memcpy(buf + 3, tlv->value, tlv->length); *len = 3 + tlv->length; } // 解码一个TLV结构体 void decode_tlv(TLV *tlv, uint8_t *buf, int len) { tlv->tag = buf[0]; tlv->length = (buf[1] << 8) | buf[2]; tlv->value = malloc(tlv->length); memcpy(tlv->value, buf + 3, tlv->length); } // 添加一个TLV链表结构体 void add_tlv(TLV_LIST **list, TLV *tlv) { TLV_LIST *new_node = malloc(sizeof(TLV_LIST)); new_node->tlv = tlv; new_node->next = NULL; if (*list == NULL) { *list = new_node; } else { TLV_LIST *cur_node = *list; while (cur_node->next != NULL) { cur_node = cur_node->next; } cur_node->next = new_node; } } // 释放一个TLV结构体 void free_tlv(TLV *tlv) { free(tlv->value); free(tlv); } // 释放一个TLV链表结构体 void free_tlv_list(TLV_LIST *list) { while (list != NULL) { TLV_LIST *cur_node = list; list = list->next; free_tlv(cur_node->tlv); free(cur_node); } } int main() { // 创建一个TLV链表结构体,并添加两个TLV结构体 TLV_LIST *tlv_list = NULL; TLV *tlv1 = malloc(sizeof(TLV)); tlv1->tag = 0x01; tlv1->length = 3; tlv1->value = (uint8_t*)malloc(3); tlv1->value[0] = 0x01; tlv1->value[1] = 0x02; tlv1->value[2] = 0x03; add_tlv(&tlv_list, tlv1); TLV *tlv2 = malloc(sizeof(TLV)); tlv2->tag = 0x02; tlv2->length = 5; tlv2->value = (uint8_t*)malloc(5); tlv2->value[0] = 0x04; tlv2->value[1] = 0x05; tlv2->value[2] = 0x06; tlv2->value[3] = 0x07; tlv2->value[4] = 0x08; add_tlv(&tlv_list, tlv2); // 编码TLV链表 int tlv_list_len = 0; TLV_LIST *cur_node = tlv_list; while (cur_node != NULL) { tlv_list_len += cur_node->tlv->length + 3; cur_node = cur_node->next; } uint8_t *tlv_list_buf = malloc(tlv_list_len); int tlv_list_pos = 0; cur_node = tlv_list; while (cur_node != NULL) { encode_tlv(cur_node->tlv, tlv_list_buf + tlv_list_pos, &(cur_node->tlv->length)); tlv_list_pos += cur_node->tlv->length; cur_node = cur_node->next; } // 解码TLV链表 cur_node = NULL; int pos = 0; while (pos < tlv_list_len) { TLV *tlv = malloc(sizeof(TLV)); decode_tlv(tlv, tlv_list_buf + pos, tlv_list_len - pos); pos += tlv->length; add_tlv(&cur_node, tlv); } // 打印结果 cur_node = cur_node; while (cur_node != NULL) { printf("Tag: %02X\n", cur_node->tlv->tag); printf("Length: %d\n", cur_node->tlv->length); printf("Value: "); for (int i = 0; i < cur_node->tlv->length; i++) { printf("%02X ", cur_node->tlv->value[i]); } printf("\n"); cur_node = cur_node->next; } // 释放内存 free(tlv_list_buf); free_tlv_list(tlv_list); free_tlv_list(cur_node); return 0; } ``` 在这个示例中,首先创建一个TLV链表结构体,并分别添加两个TLV结构体。然后编码TLV链表成一个字节数组,并将其解码成另一个TLV链表结构体。最后打印结果并释放内存。 通过这个示例,可以看到使用C语言实现TLV协议是比较简单的。需要注意的是,由于TLV协议的长度是使用两个字节表示的,因此需要考虑其字节序。在本例中,假设使用的是大端字节序。在实际应用中,需要根据具体情况选择适当的字节序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值