前言:

本人从事于Linux应用开发(音视频应用方向),现在主要是负者AI摄像头的开发,在学音视频的途中,虽然是个小白,但是更愿意把自己所学音视频的一些知识分享给大家,以后每周都会更新哦!

本期介绍的是用c语言对Jt808协议解析,要对协议进行解析,首先我们要知道jt808的协议基础以及数据传输过程的信息组成。

一、数据类型:

   数据类型                         描述及要求
        BYTE         无符号单字节整型(字节,8 位)
        WORD         无符号双字节整型(字,16 位)
        DWORD         无符号四字节整型(双字,32 位)
        BYTE[n]         n 字节
        BCD[n]         8421 码,n 字节
        STRING         GBK 编码,若无数据,置空

传输规则协议采用大端模式(bir-endian)的网络字节序来传递字和双字。

约定如下:

----------字节(BYTE)的传输约定:按照字节流的方式传输;

----------字(WORD)的传输约定:先传递高八位,再传递低八位;

----------双字节(DWORD)的传输约定:先传递高 24 位,然后传递高 16 位,在 传递高八位,最后传递低八位。

这里要区分一下字节序的大小端模式:

Little endian:将低序字节存储在起始地址。

Big endian:将高序字节存储在起始地址。

举个例子:int a = 0x12345678;

0x78属于低地址,而0x12属于高地址

如果是大端模式,那输出方式是0x12 0x34 0x56 0x78,如果是小端模式,那么就是0x78 0x56 0x34 0x12

这个很容易理解,人类读写数据的习惯是大端字节序,而小端字节序是反着人类来的。

而我们网络字节序是大端模式,因此要转为大端模式。

注:

**大端小端是对于高于一个字节的数据类型来说的,比如说int,short等。char 类型的话就不存在大小端的问题。**

- 服务器一般是大端的(因为网络字节序是大端的,服务器为大端的话就不用修改了) 个人的电脑一般是小端的。
- 主机向网络上传输的数据,只要大于两个字节,都需要将主机字节序转换成网络字节序。

大端转小端的一些api。

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

二、消息的组成

1.每条消息由标位头、消息头、消息体和校验码组成,消息结构如图所示:

标识位 消息头 消息体 检验码 标识位

2.标识位

标识位采用 0x7e 表示,若校验码、消息头以及消息体中出现 0x7e,则要进行转义处理,转 义规则定义如下:

0x7e ←→0x7d 后紧跟一个 0x02; 0x7d ←→0x7d 后紧跟一个 0x01

转义处理过程如下:

发送消息时:消息封装→计算机并填充校验码→转义;

接收消息时:转移还原→验证校验码→解析消息。

示例:

发送一包内容为 0x30 0x7e 0x08 0x7d 0x55 的数据包,则经过封装如下:0x7e 0x30 0x7d 0x02 0x08 0x7d 0x01 0x55 0x7e。

三、消息头

消息头内容

起始字节         字段 数据类型                 说明
        0       消息 ID    WORD
        2     消息体属性    WORD 消息体属性格式结构见图 2
        4     终端手机号    BCD[6] 根据安装终端自身的手机号转换。手机号 不足 12 位,则在前补充数字,大陆手机 号补充数字 0,港澳台则根据其区号进行 位数补充
        10     消息流水号     WORD 按发送顺序从 0 开始循环累加
        12     消息包封装项 如果消息体属性中相关标识位确定消息分 包处理,则该项有内容,否则无该项

消息体内容:

实时音视频传输请求

消息 ID:0x9101

报文类型:信令数据报文。平台向终端设备请求实时音视频传输,包括实时视频传输、主动发起双向语音对讲、单向监听、向所有终端广播语音和特定透传等。消息体数据格式见如下表。 终端在收到此消息后回复视频终端通用应答,然后通过对应的服务器IP地址和端口号建立传输链路,然后按照音视频流传输协议传输相应的音视频流数据。

实时音视频传输请求数据格式

起 始 字 节         字 段 数据类型         描述及要求
        0 服务器 IP 地址长度         BYTE         长度 n        
        1     服务器 IP 地址 STRING         实时视频服务器 IP 地址
        1 + n 服务器视频通道监听端口号 (TCP) WORD         实时视频服务器 TCP 端口号
        3 + n 服务器视频通道监听端口号 (UDP) WORD 实时视频服务器 UDP 端口号        
        5 + n         逻辑通道号 BYTE
        6 + n         数据类型 BYTE 0:音视频,1:视频,2:双向对讲,3:监听, 4:中心广播,5:透传
        7 + n         码流类型 BYTE 0:主码流,1:子码流

平台收到视频终端的特殊报警后,应无须等待人工确认即主动下发本条指令,启动实时音视频传输。

通过阅读以上信息得知,我们解析先要从消息头跟消息体的内容出发,可以分别定义消息头跟消息体的结构体,里面的数据类型要跟以上的信息一致哦!

要解析这个协议,首先我将这些字符都放进数组里,然后封装了一个Jt808app函数,定义一个char *p指向这个数组,然后对指针p进行遍历,就能达到快速获得某个字符的地址跟值的效果,与数组相比,用for循环遍历显得比较麻烦。因此用指针指向数组的方法是最简便的。然后用memcpy(不能用strcpy)将地址赋给刚刚结构体定义的地址。然后用*取内容符号把值取出来,接着打印出来每个数据类型。

strcpy和memcpy的区别:

复制的内容不同,strcpy只能复制字符串,而memcpy可以复制任意内容,例如:字符数组、整型、结构体、类等
复制的方法不同,strcpy不需要指定长度,它遇到被复制字符的串结束符“\0”才结束,所以容易溢出。memcpy是根据其第三个参数决定复制的长度
用途不同,通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy

void *memcpy(void *dest, const void *src, size_t n);

功能:从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中

char* strcpy(char* dest, const char* src)

这个是需要解析的数据:

7e 91 01 00     16 01 33 04     70 54 35 42     cb 0e 31 32     
30 2e 37 38     2e 32 30 35     2e 31 37 38     1e 77 00 00     
01 00 01 54     7e

以下是我写的代码读不懂看我的注释哦!

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <assert.h>
#include <arpa/inet.h>struct msgHead
{unsigned char msgHeader;   //消息头unsigned short msgId;      //消息Idunsigned short msgAddr;       //消息属性unsigned char BCD[6];     //终端手机号unsigned short serialNum;   //消息流水号unsigned short msgPacket; //消息包封装项unsigned char checkBit;     //检验位unsigned char msgTail;     //消息尾
};struct msgBody
{unsigned char serverIpLen;      //服务IP长度char serverIp[12];             //IP地址unsigned short msgTcpPort;       //TCP端口号unsigned short msgUdpPort;     //UPD端口号unsigned char channelNum;    //逻辑通道号  unsigned char dataType;     //数据类型unsigned char dataRate;    //码流类型
};char charSave[128] = {'\0'};//fgetc每次读一个十六进制的字符,hexHandler功能是将两个十六进制的字符合成一个,并放到数组里
int hexHandler(FILE *fp)
{uint32_t cnt = 0;int out;int i = 0;uint32_t a = 0;unsigned char data[3];while (1){out = fgetc(fp);//printf("out = 0x%02x\n",out);if (out == EOF){if (a == 1){fputc(data[0], fp);cnt++;}break;}else if (out == ' ' || out == '\n' || out == '\r' || out == '\t' ){//out = 0xff;continue;}else if (out >= '0' && out <= '9'){out = out - '0';}else if (out >= 'a' && out <= 'f'){out = out - 'a' + 10;}else if (out >= 'A' && out <= 'F'){out = out - 'A' + 10;}else {printf("存在其他文件!\n");fclose(fp);return -1;}data[a] = out;a++;if (a == 2){a = 0;charSave[i] = (data[0] *16) + data[1];i++;cnt++;}}fclose(fp);return cnt;}
//JT808协议解析
void Jt808app()
{char data[128];int Len;int i;char *p = charSave;struct msgHead saveMsgHeader;struct msgBody saveMsgBody;printf("======================================================================================\n");printf("消息头解析begin:\n");//一、消息头//1.消息头  saveMsgHeader.msgHeader = *p;   printf("消息头:0x%02x\n",saveMsgHeader.msgHeader);p=p+1;//2.消息idmemcpy(&saveMsgHeader.msgId,p,2);printf("消息Id:0x%04x\n",htons(saveMsgHeader.msgId));p = p+2;//3.消息属性 memcpy(&saveMsgHeader.msgAddr,p,2);printf("消息属性:0x%04x\n",htons(saveMsgHeader.msgAddr));p = p+2;//4.终端手机号memcpy(&saveMsgHeader.BCD,p,6);printf("终端手机号:");for(i = 0;i<6;i++){printf("%02x",saveMsgHeader.BCD[i]);}p = p+6;printf("\n");//5.流水号memcpy(&saveMsgHeader.serialNum,p,2);  printf("流水号:0x%4x\n",htons(saveMsgHeader.serialNum));p = p+2;printf("======================================================================================\n");printf("消息体解析begin:\n");
//二、消息体//1.服务IP长度:memcpy(&saveMsgBody.serverIpLen,p,1);Len = (int)*p;//printf("len:%d\n",Len);printf("服务IP长度:%d\n",(int)saveMsgBody.serverIpLen);p=p+1;//2.服务IP:memcpy(&saveMsgBody.serverIp,p,Len);  printf("服务IP:%s\n",saveMsgBody.serverIp);p = p+Len;//3.TCP端口号memcpy(&saveMsgBody.msgTcpPort,p,2);  printf("TCP端口号:%d\n",htons(saveMsgBody.msgTcpPort));p = p+2;
//4.UDP端口号memcpy(&saveMsgBody.msgUdpPort,p,2); printf("UDP端口号:%d\n",htons(saveMsgBody.msgUdpPort));p = p+2;//5.逻辑通道号memcpy(&saveMsgBody.channelNum,p,1); printf("逻辑通道号:%02x\n",saveMsgBody.channelNum);p = p+1;//6.数据类型saveMsgBody.dataType= (int)*p;if(saveMsgBody.dataType == 0){printf("数据类型:音视频\n");}else if(saveMsgBody.dataType == 1){printf("码流类型:视频\n");}else if(saveMsgBody.dataType == 2){printf("码流类型:双向对讲\n");}else if(saveMsgBody.dataType == 3){printf("码流类型:监听\n");}else if(saveMsgBody.dataType == 4){printf("码流类型:中心广播\n");}else if(saveMsgBody.dataType == 5){printf("码流类型:透传\n");}p = p+1;
//7.码流类型saveMsgBody.dataType = (int)*p;if(saveMsgBody.dataType == 0){printf("码流类型:主码流\n");}else if(saveMsgBody.dataType == 1){printf("码流类型:子码流\n");}  p = p+1;//检验位memcpy(&saveMsgHeader.checkBit,p,1);printf("检验位:0x%02x\n",saveMsgHeader.checkBit);p = p+1;//消息尾saveMsgHeader.msgTail = *p; printf("消息尾:0x%02x\n",saveMsgHeader.msgTail);printf("======================================================================================\n");printf("解析完毕!\n");
}
//打印数组,我是用来测试hexHandler这个函数有没将十六进制的数据放到数组里。
void printArry(int cnt)
{int i;printf("printArry begin\n");for(i = 0;i < cnt;i++){printf("%d = 0x%02x\n",i,(unsigned char)charSave[i]);}printf("printArry %d byte end\n",cnt);}int main()
{FILE* fp;int num;char fileName[12] = "9101.txt";fp = fopen(fileName, "r");if (fp ==NULL){printf("9101.txt cannot be open!\n");return -1;}num = hexHandler(fp);  Jt808app();//printArry(num);return 0;
}

实现效果如下图:

北斗系统学习—JT808协议用C语言解析相关推荐

  1. 北斗系统学习:JTT808协议初步解析

    本文学习部标(交通运输部)JT/T 808,并使用 Golang 语言解析.当然,仅使用位置数据进行演示,所以只是一个开端(是否有后续,暂未知).本文不是科普,因此不会详细列出协议字段说明,可参考文后 ...

  2. 系统学习-----NFS协议

    文章目录 NFS简介 NFS协议模型 RPC协议 NFS相关包和工具 NFS服务概览 NFS服务端配置 NFS客户端 NFS实验部署 NFS简介 网络文件系统,采用CS架构的 分布式计算系统中的一部分 ...

  3. 华为鸿蒙系统学习笔记6-方舟编译器深度解析

    8 月 9 日,华为开发者大会鸿蒙OS正式发布,这是基于微内核的全场景分布式OS.能实现模块化解耦,对应不同设备(智慧屏.穿戴设备.车机.智能音箱.手机)可弹性部署.华为消费者业务 CEO 余承东描述 ...

  4. MySQL,Oracle系统学习,以及SQL语言-----数据库篇学习笔记

    Handouts MySQL和Oracle系统学习 一. 开篇立意(~~~~必看,有说明~~~~) 二. Oracle 篇 数据库存在之意义 基础概念(必须看,后面不会说明!!!) Oracle管理系 ...

  5. 想系统学习GO语言(Golang),能推荐几本靠谱的书吗?

    以下内容来自知乎: 链接:https://www.zhihu.com/question/30461290 学习任何一门语言,都要学习好基础,把基础打牢,那些框架对你来说都是工具,你自己的基础好,懂得了 ...

  6. lua语言学习之自定义wireshark插件来解析自定义协议

    lua语言学习之自定义wireshark插件来解析自定义协议 关于wireshark这个抓包工具 关于lua 使用lua写wireshark插件 wireshark接口文档 如何在wireshark使 ...

  7. 系统学习Go语言,有这几本书就够了!

    最新个人博客 shankusu.me 以下内容转载自 https://tonybai.com/2020/11/04/the-recommend-books-list-for-learning-go/ ...

  8. java div2_系统学习 javaweb2----HTML语言2

    感想:学习javaweb之路,任重而道远. 学习笔记: 5.表格标签 5.1 表格标签,用于效果中定义一个表格 5.2 表格的行标签,用于在效果中定义一个表格行 5.3 第一步:定义一个表格 第二步: ...

  9. 综述 | 北斗系统应用趋势分析

    来源:智绘科服 初审:张艳玲 复审:宋启凡 终审:金   君 一.前言 2020年6月23日,北斗三号最后一颗组网卫星成功发射.2020年7月31日,北斗三号建成暨开通仪式举行,北斗三号全球卫星导航系 ...

最新文章

  1. CentOS镜像下载地址
  2. JNI学习开始篇 基础知识 数据映射及学习资料收集
  3. 什么叫做类数组对象?
  4. 子div在父div中置底
  5. Oracle中NUMBER类型如果不指定长度和小数点精度默认是多长
  6. jq分页 不刷新页面_jQuery无刷新分页完整实例代码
  7. 无法恢复,欧洲云服务巨头数据中心起火
  8. radio 取值赋值 亲测有用实效
  9. JavaScript事件冒泡和事件委托
  10. Hello, world!
  11. 关于python项目路径导入自己写的库出错的一点思考
  12. 【2022最新】Vscode配置Python环境Leetcode刷题指南
  13. qt java tcp_incomingConnection qtcpsocket
  14. hw叠加层开还是不开_停用hw叠加层有什么用
  15. banner图的开发
  16. ORACLE ODBC驱动安装
  17. 图书管理系统(I/O)
  18. 谈学习中的改变——有病要诊断,有药要服用
  19. linux——awk(3):awk变量
  20. 数据分析金庸武侠经典人物,我们喜欢江湖中的这群人

热门文章

  1. NOI Online 2020 Round3 滚粗记
  2. 案例 | 荔枝微课基于 kubernetes 搭建分布式压测系统
  3. CSS3伪类选择器:nth-child(n)及:nth-of-type(n)使用区别探究总结
  4. 基于WEB 的实时事件通知方案
  5. 提供一个 无限存储 空间 免费网盘
  6. 1. R语言中grep函数和gsub()函数的使用
  7. echart地图知识点
  8. vim编辑页面怎么退出_linux系统中如何进入退出vim编辑器
  9. 我在前锋培训的日子第二天
  10. Scala:函数与匿名函数