海思3559万能平台搭建:协议的采集和解析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 海思3559万能平台搭建:协议的采集和解析

前言

 做应用的话就难免和协议打交道,最基础简单的协议可能像单片机一样通篇的ifelse,虽然逻辑严谨,但是不论编写还是理解难度都非常头疼。且一旦更改协议,直呼骂人。介绍一种协议定长时的处理方法,解析包头内容和校验,这样搭配着我们之前提到的配置文件,理论上甚至变协议可以做到代码和变文档一样简单!

原理

 既然是定长的协议,那就可以使用一个固定容量的队列用来缓存接收到的数据,队列容量等于一帧数据的大小,每来一个数据就将数据往队列里面加,当完整接收到一帧数据时此时队列中的全部数据也就是一帧完整的数据,因此只需要判断队列是否是数据校验头,队列尾是否是数据校验尾就可以得知当前是否已经接收到了一帧完整的数据,然后在将数据从队列中取出即可。原理图如下:

 每来一个数据就往队列里面加:

9831d28417ac4f6ea1f69533b04bf120.png

 当接收到一帧完整数据时队列头和数据校验头重合:

5a118482441c45e8a46c803bae6d13bc.png

 此时只需要从队列中取出有效数据即可。

 如果有数据尾校验,仅仅只需要添加一个校验尾即可,如下图所示:

67f0cb33eede42b3b42b7ba7774cf919.png

代码

 首先需要一个队列,为了保证通用性,队列底层使用类似于双向链表的实现(当然也可以使用数组实现),需要封装的结构有队列容量、队列大小、队头节点和队尾节点,需要实现的操作有队列初始化、数据入队、数据出队、清空队列和释放队列,

queue.h

#ifndef _QUEUE_H_
#define _QUEUE_H_
#ifndef NULL
#define NULL  ((void *)0)
#endif
typedef unsigned char uint8;
/* 队列节点 */
typedef struct Node
{
  uint8 data;
  struct Node *pre_node;
  struct Node *next_node;
} Node;
/* 队列结构 */
typedef struct Queue
{
  uint8 capacity;     // 队列总容量
  uint8 size;         // 当前队列大小
  Node *front;        // 队列头节点
  Node *back;         // 队列尾节点
} Queue;
/* 初始化一个队列 */
Queue *init_pqueue(uint8 _capacity);
/* 数据入队 */
uint8 en_queue(Queue *_queue, uint8 _data);
/* 数据出队 */
uint8 de_queue(Queue *_queue);
/* 清空队列 */
void clear_queue(Queue *_queue);
/* 释放队列 */
void release_queue(Queue *_queue);
#endif

queue.c

#include <stdlib.h>
#include "parser.h"
/**
 * 初始化一个队列
 *
 * @_capacity: 队列总容量
 */
Queue *init_pqueue(uint8 _capacity)
{
  Queue *queue = (Queue *)malloc(sizeof(Queue));
  queue->capacity = _capacity;
  queue->size = 0;
  return queue;
}
/**
 * 数据入队
 *
 * @_queue: 队列
 * @_data: 数据
 **/
uint8 en_queue(Queue *_queue, uint8 _data)
{
  if(_queue->size < _queue->capacity)
  {
  Node *node = (Node *)malloc(sizeof(Node));
  node->data = _data;
  node->next_node = NULL;
        if(_queue->size == 0)
        {
            node->pre_node = NULL;
            _queue->back = node;
            _queue->front = _queue->back;
        }
        else
        {
            node->pre_node = _queue->back;
            _queue->back->next_node = node;
            _queue->back = _queue->back->next_node;
        }
  _queue->size++;
  }
  else
  {
  Node *temp_node = _queue->front->next_node;
  _queue->front->pre_node = _queue->back;
  _queue->back->next_node = _queue->front;
  _queue->back = _queue->back->next_node;
  _queue->back->data = _data;
  _queue->back->next_node = NULL;
  _queue->front = temp_node;
  }
  return _queue->size-1;
}
/**
 * 数据出队
 *
 * @_queue: 队列
 *
 * @return: 出队的数据
 */
uint8 de_queue(Queue *_queue)
{
    uint8 old_data = 0;
    if(_queue->size > 0)
    {
        old_data = _queue->front->data;
        if(_queue->size == 1)
        {
            free(_queue->front);
            _queue->front = NULL;
            _queue->back = NULL;
        }
        else
        {
            _queue->front = _queue->front->next_node;
            free(_queue->front->pre_node);
            _queue->front->pre_node = NULL;
        }
        _queue->size--;
    }
    return old_data;
}
/**
 * 清空队列
 *
 * @_queue: 队列
 */
void clear_queue(Queue *_queue)
{
    while(_queue->size > 0)
    {
        de_queue(_queue);
    }
}
/**
 * 释放队列
 *
 * @_queue: 队列
 */
void release_queue(Queue *_queue)
{
    clear_queue(_queue);
    free(_queue);
    _queue = NULL;
}

 其次是解析器,需要封装的结构有解析数据队列、数据校验头、数据校验尾、解析结果以及指向解析结果的指针,需要实现的操作有解析器初始化、添加数据解析、获取解析结果、重置解析器和释放解析器,具体代码如下:

parser.h

#ifndef _PARSER_H_
#define _PARSER_H_
#include "queue.h"
typedef enum
{
    RESULT_FALSE,
    RESULT_TRUE
} ParserResult;
/* 解析器结构 */
typedef struct DataParser
{
    Queue *parser_queue; // 数据解析队列
    Node *resule_pointer;// 解析结果数据指针
    uint8 *data_header;// 数据校验头指针
    uint8 header_size;// 数据校验头大小
    uint8 *data_footer;// 数据校验尾指针
    uint8 footer_size;// 数据校验尾大小
    uint8 result_size;// 解析数据大小
    ParserResult parserResult;// 解析结果
} DataParser;
/* 初始化一个解析器 */
DataParser *parser_init(uint8 *_data_header, uint8 _header_size, uint8 *_data_footer, uint8 _foot_size, uint8 _data_frame_size);
/* 将数据添加到解析器中进行解析 */
ParserResult parser_put_data(DataParser *_parser, uint8 _data);
/* 解析成功后从解析器中取出解析结果 */
int parser_get_data(DataParser *_parser, uint8 _index);
/* 重置解析器 */
void parser_reset(DataParser *_parser);
/* 释放解析器 */
void parser_release(DataParser *_parser);
#endif

parser.c

#include <stdlib.h>
#include "parser.h"
/**
 * 初始化一个解析器
 *
 * @_data_header: 数据头指针
 * @_header_size: 数据头大小
 * @_data_footer: 数据尾指针
 * @_foot_size: 数据尾大小
 * @_data_frame_size: 一帧完整数据的大小
 *
 * @return: 解析器
 */
DataParser *parser_init(uint8 *_data_header, uint8 _header_size, uint8 *_data_footer, uint8 _foot_size, uint8 _data_frame_size)
{
    if((_header_size+_foot_size) > _data_frame_size || (_header_size+_foot_size) == 0)
        return NULL;
    DataParser *parser = (DataParser *)malloc(sizeof(DataParser));
    parser->parser_queue = init_pqueue(_data_frame_size);
    parser->resule_pointer = NULL;
    parser->data_header = _data_header;
    parser->header_size = _header_size;
  parser->data_footer = _data_footer;
  parser->footer_size = _foot_size;
    parser->result_size = _data_frame_size - parser->header_size - parser->footer_size;
    parser->parserResult = RESULT_FALSE;
    while(_data_frame_size-- > 0)
    {
        en_queue(parser->parser_queue, 0);
    }
    return parser;
}
/**
 * 将数据添加到解析器中进行解析
 *
 * @_parser: 解析器
 * @_data: 要解析的数据
 *
 * @return: 当前解析结果,返回 RESULT_TRUE 代表成功解析出一帧数据
 */
ParserResult parser_put_data(DataParser *_parser, uint8 _data)
{
    uint8 i;
    Node *node;
  if(_parser == NULL)
  return RESULT_FALSE;
    en_queue(_parser->parser_queue, _data);
  /* 校验数据尾 */
  node = _parser->parser_queue->back;
  for(i = _parser->footer_size; i > 0; i--)
  {
  if(node->data != _parser->data_footer[i-1])
            goto DATA_FRAME_FALSE;
        node = node->pre_node;
  }
  /* 校验数据头 */
    node = _parser->parser_queue->front;
    for(i = 0; i < _parser->header_size; i++)
    {
        if(node->data != _parser->data_header[i])
            goto DATA_FRAME_FALSE;
        node = node->next_node;
    }
    if(_parser->resule_pointer == NULL && _parser->result_size > 0)
        _parser->resule_pointer = node;
    if(_parser->parserResult != RESULT_TRUE)
      _parser->parserResult = RESULT_TRUE;
    return _parser->parserResult;
DATA_FRAME_FALSE:
    if(_parser->resule_pointer != NULL)
        _parser->resule_pointer = NULL;
    if(_parser->parserResult != RESULT_FALSE)
        _parser->parserResult = RESULT_FALSE;
    return _parser->parserResult;
}
/**
 * 解析成功后从解析器中取出解析结果
 *
 * @_parser: 解析器
 * @_index: 解析结果集合中的第 _index 个数据
 *
 * @return: 获取解析成功的数据,返回 -1 代表数据获取失败
 */
int parser_get_data(DataParser *_parser, uint8 _index)
{
    Node *node;
    if(_parser == NULL
  || _parser->parserResult != RESULT_TRUE
    || _index >= _parser->result_size
    || _parser->resule_pointer == NULL)
        return -1;
    node = _parser->resule_pointer;
    while(_index > 0)
    {
        node = node->next_node;
        _index--;
    }
    return node->data;
}
/**
 * 重置解析器
 *
 * @_parser: 解析器
 */
void parser_reset(DataParser *_parser)
{
  uint8 _data_frame_size;
  if(_parser == NULL)
  return;
  _data_frame_size = _parser->parser_queue->size;
  while(_data_frame_size-- > 0)
    {
        en_queue(_parser->parser_queue, 0);
    }
    _parser->resule_pointer = NULL;
    _parser->parserResult = RESULT_FALSE;
}
/**
 * 释放解析器
 *
 * @_parser: 解析器
 */
void parser_release(DataParser *_parser)
{
  if(_parser == NULL)
  return;
    release_queue(_parser->parser_queue);
    free(_parser);
    _parser = NULL;
}

校验尾

 校验尾大多数不会是固定的,所以我们在添加时校验尾是空着的,当数据处理,收到后在单独计算,比如最简单的累加校验。累加除去头和尾的所有数据,取低8位,就可以申明一个u8的变量,然后收到后去头去尾累加,和尾比较,一样的话进行处理,不一样则这一包清空不作数


HI_VOID * eth_client_recv_task(HI_VOID *arg)
{
  cpu_set_t mask;//cpu核的集合
    cpu_set_t get;//获取在集合中的cpu
    int num = sysconf(_SC_NPROCESSORS_CONF);
    printf("frame_check_task:system has %d processor(s)\n", num);
    CPU_ZERO(&mask);//置空
    CPU_SET(0, &mask);//设置亲和力值
    if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0)//设置线程CPU亲和力
    {
        fprintf(stderr, "set thread affinity failed\n");
    }
    if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0)//获取线程CPU亲和力
    {
        fprintf(stderr, "get thread affinity failed\n");
    }
  int connect_fd = -1;
    int recv_len,i;
  struct sockaddr_in server;
  socklen_t saddrlen = sizeof(server);
  uint8 data_header[] = {0x55, 0xAA};
  unsigned char addchk=0;
  // uint8 data_header[] = {0x55, 0xAA};
  // printf("\nbeforen DataParser\n ");
  DataParser *data_parser = parser_init(data_header, sizeof(data_header), NULL, 0, CMD_LENGTH);
  // printf("\nDataParser INIT SUCCESS\n ");
  char* tmp_cmd=(char*)&g_trk_cmd;
  memset(&server, 0, sizeof(server));
  connect_fd = socket(AF_INET, SOCK_STREAM, 0);
  if (connect_fd < 0)
  {
  printf("socket error!\n");
  // return NULL;
  }
    printf("\nETH CLIENT RECV TEST\n");
  server.sin_family = AF_INET;
  server.sin_port = htons(SERVER_PORT);
  server.sin_addr.s_addr = inet_addr(SERVER_IP);
  if (connect(connect_fd, (struct sockaddr *)&server, saddrlen) < 0)
  {
  printf("connect failed!\n");
  // return -1;
  }
  char recvbuf[1024]={0};
  while (1)
  {
  recv_len = read(connect_fd, recvbuf, sizeof(recvbuf));
        if(recv_len)
        {
            // printf("\nEth:recv origin data: ");
            // recvbuf[len] = '\0';  
            for (i = 0;i < recv_len ;i++)
            {
                // printf("%02x  ",recvbuf[i]);  
    if(parser_put_data(data_parser, recvbuf[i]) == RESULT_TRUE)
    {
      // printf("成功解析出一帧数据...\n");
      // /* 一位一位取出解析后的数据 */
      // g_trk_cmd.length  = parser_get_data(data_parser, 0);
      // g_trk_cmd.CmdType = parser_get_data(data_parser, 1);
      // for (int ii = 0; ii < DATA_LENGTH ;ii++)
      // {
      //  g_trk_cmd.Para[ii] = parser_get_data(data_parser, 2+ii);
      // }
      tmp_cmd=(char*)&g_trk_cmd;
      // memset_s(tmp_cmd, sizeof(g_trk_cmd)-sizeof(data_header), 0, sizeof(g_trk_cmd)-sizeof(data_header));
      addchk = 0;
      for(int jj=0;jj<sizeof(g_trk_cmd)-sizeof(data_header);jj++)
      {
      tmp_cmd[jj+2]=parser_get_data(data_parser, jj);
      printf("%x ",tmp_cmd[jj+2]);
      }
      printf("\n");
      for(int kk = 2;kk < sizeof(g_trk_cmd)-1;kk++)
      {
      // addchk += parser_get_data(data_parser, kk);
       addchk+=tmp_cmd[kk];
       printf("%x ",tmp_cmd[kk]);
      }
      printf("\n");
      printf("addchk is %x\n",addchk);
      g_trk_cmd.addchk = parser_get_data(data_parser,(CMD_LENGTH-1-2));//第19个数,从0开始计18,故-1,又因为去头在-2
      printf("g_trk_cmd.addchk is %x\n",g_trk_cmd.addchk);
      if(addchk != g_trk_cmd.addchk )
      {
      memset_s(((uint8_t*)&g_trk_cmd)+2,
                        sizeof(g_trk_cmd)-sizeof(data_header),
                        0,
                        sizeof(g_trk_cmd)-sizeof(data_header));
                  continue;
      }
      g_trk_cmd.length  = tmp_cmd[2];
      printf(" tmp_cmd[2]is %x\n",tmp_cmd[2]);
      g_trk_cmd.CmdType = tmp_cmd[3];
      printf(" tmp_cmd[3]is %x\n",tmp_cmd[3]);
      for (int ii = 0; ii < DATA_LENGTH ;ii++)
      {
      g_trk_cmd.Para[ii] = tmp_cmd[ii+4];;
      }
      // printf("数据长度是0x%x\n", parser_get_data(data_parser, 0));
      // printf("功能码是0x%x\n", parser_get_data(data_parser, 1));
      // printf("具体功能是0x%x\n\n\n", parser_get_data(data_parser, 2));
    }
            }
            // printf("\n"); 
        }
  protocol_parser();
  }
  close(connect_fd);
  return 0;
}

效果

 可以看到 ,如果没按照协议发送的,只会打印原始数据,按照协议发送的,就会按照我们预设将包拆开,方便提取功能码和数据内容

8e183e272a344363a562df71da633848.png

补充

 结合之前的配置文件,协议规定内容均由配置文件给出的话,一定程度上可以做到功能更改而不需要重新编译应用只需要更改配置文件


相关文章
|
29天前
|
运维 监控 负载均衡
探索微服务架构下的服务治理:动态服务管理平台深度解析
探索微服务架构下的服务治理:动态服务管理平台深度解析
|
3月前
|
域名解析 存储 网络协议
深入解析网络通信关键要素:IP 协议、DNS 及相关技术
本文详细介绍了IP协议报头结构及其各字段的功能,包括版本、首部长度、服务类型、总长度、标识、片偏移、标志、生存时间(TTL)、协议、首部检验和等内容。此外,还探讨了IP地址的网段划分、特殊IP地址的应用场景,以及路由选择的大致流程。最后,文章简要介绍了DNS协议的作用及其发展历史,解释了域名解析系统的工作原理。
142 5
深入解析网络通信关键要素:IP 协议、DNS 及相关技术
|
2月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
165 3
|
3月前
|
机器学习/深度学习 人工智能 自然语言处理
Hugging Face 论文平台 Daily Papers 功能全解析
【9月更文挑战第23天】Hugging Face 是一个专注于自然语言处理领域的开源机器学习平台。其推出的 Daily Papers 页面旨在帮助开发者和研究人员跟踪 AI 领域的最新进展,展示经精心挑选的高质量研究论文,并提供个性化推荐、互动交流、搜索、分类浏览及邮件提醒等功能,促进学术合作与知识共享。
|
25天前
|
监控 网络协议 网络性能优化
网络通信的核心选择:TCP与UDP协议深度解析
在网络通信领域,TCP(传输控制协议)和UDP(用户数据报协议)是两种基础且截然不同的传输层协议。它们各自的特点和适用场景对于网络工程师和开发者来说至关重要。本文将深入探讨TCP和UDP的核心区别,并分析它们在实际应用中的选择依据。
53 3
|
5天前
|
传感器
Modbus协议深入解析
Modbus协议是由Modicon公司(现施耐德电气)于1979年发明的串行通信协议,主要用于工业自动化系统中的PLC通信。本文深入解析了Modbus协议的主从模式、数据类型(线圈、离散输入、保持寄存器、输入寄存器)、帧结构和通信过程,并介绍了其应用场景和重要性。
13 0
|
1月前
|
网络协议 网络安全 网络虚拟化
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算
本文介绍了十个重要的网络技术术语,包括IP地址、子网掩码、域名系统(DNS)、防火墙、虚拟专用网络(VPN)、路由器、交换机、超文本传输协议(HTTP)、传输控制协议/网际协议(TCP/IP)和云计算。通过这些术语的详细解释,帮助读者更好地理解和应用网络技术,应对数字化时代的挑战和机遇。
89 3
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
医疗行业的语音识别技术解析:AI多模态能力平台的应用与架构
AI多模态能力平台通过语音识别技术,实现实时转录医患对话,自动生成结构化数据,提高医疗效率。平台具备强大的环境降噪、语音分离及自然语言处理能力,支持与医院系统无缝集成,广泛应用于门诊记录、多学科会诊和急诊场景,显著提升工作效率和数据准确性。
|
29天前
|
负载均衡 网络协议 算法
OSPF与其他IGP协议的比较:全面解析与应用场景
OSPF与其他IGP协议的比较:全面解析与应用场景
43 0
|
1月前
|
供应链 安全 BI
CRM系统功能深度解析:为何这些平台排名靠前
本文深入解析了市场上排名靠前的CRM系统,如纷享销客、用友CRM、金蝶CRM、红圈CRM和销帮帮CRM,探讨了它们在功能性、用户体验、集成能力、数据安全和客户支持等方面的优势,以及如何满足企业的关键需求,助力企业实现数字化转型和业务增长。

推荐镜像

更多