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

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 海思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

补充

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


相关文章
|
28天前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
87 3
|
2月前
|
域名解析 存储 网络协议
深入解析网络通信关键要素:IP 协议、DNS 及相关技术
本文详细介绍了IP协议报头结构及其各字段的功能,包括版本、首部长度、服务类型、总长度、标识、片偏移、标志、生存时间(TTL)、协议、首部检验和等内容。此外,还探讨了IP地址的网段划分、特殊IP地址的应用场景,以及路由选择的大致流程。最后,文章简要介绍了DNS协议的作用及其发展历史,解释了域名解析系统的工作原理。
113 5
深入解析网络通信关键要素:IP 协议、DNS 及相关技术
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
Hugging Face 论文平台 Daily Papers 功能全解析
【9月更文挑战第23天】Hugging Face 是一个专注于自然语言处理领域的开源机器学习平台。其推出的 Daily Papers 页面旨在帮助开发者和研究人员跟踪 AI 领域的最新进展,展示经精心挑选的高质量研究论文,并提供个性化推荐、互动交流、搜索、分类浏览及邮件提醒等功能,促进学术合作与知识共享。
|
7天前
|
机器学习/深度学习 人工智能 自然语言处理
医疗行业的语音识别技术解析:AI多模态能力平台的应用与架构
AI多模态能力平台通过语音识别技术,实现实时转录医患对话,自动生成结构化数据,提高医疗效率。平台具备强大的环境降噪、语音分离及自然语言处理能力,支持与医院系统无缝集成,广泛应用于门诊记录、多学科会诊和急诊场景,显著提升工作效率和数据准确性。
|
10天前
|
供应链 安全 BI
CRM系统功能深度解析:为何这些平台排名靠前
本文深入解析了市场上排名靠前的CRM系统,如纷享销客、用友CRM、金蝶CRM、红圈CRM和销帮帮CRM,探讨了它们在功能性、用户体验、集成能力、数据安全和客户支持等方面的优势,以及如何满足企业的关键需求,助力企业实现数字化转型和业务增长。
|
2月前
|
前端开发 JavaScript 安全
深入解析 http 协议
HTTP(超文本传输协议)不仅用于传输文本,还支持图片、音频和视频等多种类型的数据。当前广泛使用的版本为 HTTP/1.1。HTTPS 可视为 HTTP 的安全增强版,主要区别在于添加了加密层。HTTP 请求和响应均遵循固定格式,包括请求行/状态行、请求/响应头、空行及消息主体。URL(统一资源定位符)用于标识网络上的资源,其格式包含协议、域名、路径等信息。此外,HTTP 报头提供了附加信息,帮助客户端和服务端更好地处理请求与响应。状态码则用于指示请求结果,如 200 表示成功,404 表示未找到,500 表示服务器内部错误等。
55 0
深入解析 http 协议
|
2月前
|
数据挖掘 BI UED
B2B 领域 CRM 平台全景解析
在快节奏的商业环境中,移动CRM应用让企业随时随地管理客户关系,成为不可或缺的利器。本文深入探讨了七款优秀移动CRM应用:销售易Mobile、Salesforce Mobile、纷享销客、Zoho CRM Mobile、HubSpot Mobile、金蝶云·星辰移动端及用友U8+移动端,详细分析了各自的优势和适用场景。企业可根据具体需求、预算和行业特点,选择最适合的移动CRM解决方案,提升销售效率与管理水平,为企业发展注入新活力。
B2B 领域 CRM 平台全景解析
|
2月前
|
数据采集 存储 JSON
从零到一构建网络爬虫帝国:HTTP协议+Python requests库深度解析
在网络数据的海洋中,网络爬虫遵循HTTP协议,穿梭于互联网各处,收集宝贵信息。本文将从零开始,使用Python的requests库,深入解析HTTP协议,助你构建自己的网络爬虫帝国。首先介绍HTTP协议基础,包括请求与响应结构;然后详细介绍requests库的安装与使用,演示如何发送GET和POST请求并处理响应;最后概述爬虫构建流程及挑战,帮助你逐步掌握核心技术,畅游数据海洋。
66 3
|
2月前
|
API 云计算 开发者
使用宜搭平台带来的便利:技术解析与实践
【9月更文第8天】随着企业信息化建设的不断深入,业务流程自动化的需求日益增长。宜搭平台作为一种高效的应用构建工具,为企业提供了快速搭建各类业务系统的可能。本文将探讨使用宜搭平台给企业和开发者带来的便利,并通过具体的代码示例展示其优势。
86 11
|
2月前
|
监控 算法 数据可视化
深入解析Android应用开发中的高效内存管理策略在移动应用开发领域,Android平台因其开放性和灵活性备受开发者青睐。然而,随之而来的是内存管理的复杂性,这对开发者提出了更高的要求。高效的内存管理不仅能够提升应用的性能,还能有效避免因内存泄漏导致的应用崩溃。本文将探讨Android应用开发中的内存管理问题,并提供一系列实用的优化策略,帮助开发者打造更稳定、更高效的应用。
在Android开发中,内存管理是一个绕不开的话题。良好的内存管理机制不仅可以提高应用的运行效率,还能有效预防内存泄漏和过度消耗,从而延长电池寿命并提升用户体验。本文从Android内存管理的基本原理出发,详细讨论了几种常见的内存管理技巧,包括内存泄漏的检测与修复、内存分配与回收的优化方法,以及如何通过合理的编程习惯减少内存开销。通过对这些内容的阐述,旨在为Android开发者提供一套系统化的内存优化指南,助力开发出更加流畅稳定的应用。
68 0

推荐镜像

更多
下一篇
无影云桌面