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

article/2025/4/28 5:31:24

       前言:

         本人从事于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        数据类型BYTE0:音视频,1:视频,2:双向对讲,3:监听, 4:中心广播,5:透传
        7 + n        码流类型BYTE0:主码流,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;
}

实现效果如下图:


http://chatgpt.dhexx.cn/article/Dsty0NJO.shtml

相关文章

TCP/IP协议(一、自己动手实现udp)

对TCP/IP协议都只是听过&#xff0c;没有仔细研究过&#xff0c;一些知识体系也比较零散&#xff0c;什么三次握手&#xff0c;四次挥手&#xff0c;滑动窗口&#xff0c;零拷贝技术等等&#xff0c;都是知识有这么个东西&#xff0c;而不知道具体是啥&#xff0c;这几天还是根…

【Linux】传输层协议:UDP和TCP

但人不能永远浪漫下去&#xff0c;那会走向自我毁灭的。浪漫都是水字旁&#xff0c;会把人淹死的。人最终还是要进入一个规则体系&#xff0c;所谓&#xff0c;随心所欲不逾矩嘛 文章目录 一、UDP协议1.端口号2.理解UDP报头3.UDP的特点&#xff08;面向数据报&#xff0c;全双…

Linux网络:UDP协议 | TCP协议

文章目录 前言端口号netstat -- pidof UDP协议TCP协议三次握手 四次挥手确认应答(ACK)机制超时重传机制连接管理机制理解TIME_WAIT状态理解 CLOSE_WAIT 状态 滑动窗口流量控制拥塞控制延迟应答捎带应答面向字节流粘包问题TCP异常情况TCP小结 基于TCP 和 UDP 的应用层协议TCP/UD…

C语言SOCKET编程指南

转载自&#xff1a;http://blog.sina.com.cn/s/blog_79b01f66010163q3.html 这篇文章完全可以作为c语言socket编程指南&#xff0c;无论在任何系统下。感谢作者fenglovel&#xff01; C语言SOCKET编程指南 1、介绍 Socket 编程让你沮丧吗&#xff1f;从man pages中很难得到有…

【socket】从计算机网络基础到socket编程——Windows Linux C语言 + Python实现(TCP+UDP)

一、部分基础知识1.1 计算机网络的体系结构1.11 互联网简介1.12 计算机网络的分类1.13 协议与网络的分层体系结构▶ 协议▶ 网络的分层体系结构 1.14 OSI 七层模型&#xff08;重要&#xff09;▶ OSI 模型的结构▶ OSI 模型各层的功能 1.15 TCP/IP 的体系结构&#xff08;重要…

C语言 C语言基础

C语言 C语言基础 一、简述 对于C语言基础相关方面的表面理解&#xff0c;简单介绍。 二、二进制 生活中常用的是十进制&#xff0c;基数0,1,2,3,4,5,6,7,8,9,。满10进1。 时钟60进制。基数0,1,2...57,58,59。满60进1。60秒为1分钟&#xff0c;60分钟为1小时。 计算机二进制&a…

炼一项专业技能c语言,C语言程序设计_安徽新华电脑专修学院

C语言程序设计&#xff0c;安徽新华电脑专修学院&#xff0c;2013年3月&#xff0c;在安徽省315国际消费者权益日纪念大会暨省消协四届三次理事会会议上&#xff0c;安徽新华电脑专修学院被省消费者协会授予“诚信单位”。 C语言程序设计&#xff0c; 并用c语言进行测试。还有b…

C++中使用UDP Socket发送字节数据

文章目录 参考代码结果展示 参考 这篇文章给的代码也能用 https://blog.csdn.net/qq_36437446/article/details/106446172 CRC代码来自https://blog.csdn.net/huijunma2010/article/details/124151471 在线计算CRC http://www.ip33.com/crc.html 代码 这里的byte是unsigned…

本机UDP收发性能测试

测试内容 测试单机版的UPD客户端和服务端之间的性能&#xff0c;UDP客户端发送数据到UDP服务端&#xff0c;并等待服务端返回&#xff0c;计算出UDP的性能 测试方法 客户端和服务端部署在同一台虚拟机器上&#xff0c;客户端启动多个线程&#xff0c;同时向服务端发送指定数量…

2020复旦大学计算机夏令营机试题

2020复旦大学计算机夏令营机试题 这个是开卷编程&#xff0c;不计入成绩&#xff0c;但是面试会问你做的情况。

复旦大学机试题题解(2017~2021)

前言 尝试把复旦大学历年的机试题做一遍&#xff0c;能做多少是多少了&#xff0c;因为没有数据集所以也不好确认是不是对的~&#xff08;题目来自王道往届学长学姐回忆以及某位热心的同学整理&#xff0c;如果了解来源后&#xff0c;我一定备注来源&#xff09; &#xff08;…

复旦大学:专硕没住宿?我们帮你建!还给补助!

去年&#xff0c;小编发过一篇文章&#xff0c;里面爆出了复旦大学等学校的专硕可能不提供住宿了&#xff1a; 北京大学 上海交通大学 复旦大学 专硕可能不提供住宿&#xff01; 后来&#xff0c;在复旦大学2019年招收攻读硕士学位研究生简章中&#xff0c;学校官方公布了不为专…

复旦大学机试题(2017-2021)

计院2021 1.完全二叉树 给定一颗二叉树&#xff0c;树的每个节点的值为一个正整数。如果从根节点到节点 N 的路径上不存在比节点 N 的值大的节点&#xff0c;那么节点 N 被认为是树上的关键节点。求树上所有的关键节点的个数。请写出程序&#xff0c;并解释解题思路。 例子&a…

三战上岸复旦工研院考研回忆(无干货)

干货链接&#xff1a; 复旦085400电子信息专硕初试经验分享 复旦085400电子信息专硕复试经验分享 复旦软院电子信息初试961真题回忆 说说一下我的考研经历&#xff0c;都是流水账&#xff0c;不涉及干货&#xff08;干货参照其他博客&#xff09;&#xff0c;如果读者能有所借…

2020年复旦大学夏令营机试题及代码(VS2019 C++)

注:代码是本菜鸡自己个儿写的,没有找到参考答案,欢迎各位大佬批评指正. 题目如下图所示&#xff08;图片来源网上&#xff09;: 第1题 该题主要考察拓扑排序.其实该算法有通用模板,但我写的时候没有意识到使用,代码不是很规范. #define _CRT_SECURE_NO_WARNINGS#include &…

复旦大学研究生机试(2019)

1. 计算机学院 今年的题目可以说是挺难的&#xff0c;第一题虽然像是送分题&#xff0c;实际上也不是很简单。第二题第三题是动态规划问题&#xff0c;而且复旦据说会卡大数&#xff0c;今年150人考生据说只有一个AC&#xff0c;大部分人只做出第一题&#xff0c;个别零分。 …

【20保研】2019年复旦大学工程与应用技术研究院全国优秀大学生夏令营通知

点击文末的阅读原文或者公众号界面左下角的保研夏令营或者公众号回复“夏令营”是计算机/软件等专业的所有保研夏令营信息集合&#xff0c;会一直更新的。 为了促进我国高校优秀大学生之间的交流、加强学生对复旦大学工研院的了解、特别是吸引优秀学生继续深造&#xff0c;探索…

复旦大学计算机专业硕士平均工资,在复旦大学当教授“月薪”是多少?这个工资条,让网友非常羡慕!...

文章原创&#xff0c;版权归本作者所有&#xff0c;欢迎个人转发分享 随着中国的高等教育发展的重视&#xff0c;很多的高校也是不负众望&#xff0c;不仅在国内知名度很高&#xff0c;在国外也享有盛誉的。 在中国知名度最高的大学就是清华、北大、复旦等高校&#xff0c;是受…

复旦计院、工研院2019机试真题及答案详解

计院 A 相隔天数 输入一个 yyyymmdd 格式的时间&#xff0c;如 20190318&#xff0c;计算与 20190205 相差的天数&#xff0c; 取绝对值&#xff0c;所有输入在 19000101 和 21000101 之间。 样例输入&#xff1a;20190208 输出&#xff1a;3 #include<iostream>us…

复旦计院、工研院2018机试真题及答案详解

计院 A 求众数 众数就是一个序列中出现次数最多的数字。 如果不唯一&#xff0c;则输出小的那个值。 给定第一个代表有几个数字。 1<n<10^5 每个数字在 int 范围内样例&#xff1a; 输入&#xff0c; &#xff08;第一个代表有几个数字&#xff09;8 10 3 8 8 …