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

2023-11-16

       前言:

         本人从事于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;		//消息Id
	unsigned 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.消息id
	memcpy(&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;
}

实现效果如下图:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

北斗系统学习—JT808协议用C语言解析 的相关文章

随机推荐

  • 人工智能学习笔记(一)Agent

    智能agent 开篇前言 agent的分类 1 简单反射agent 2 基于状态的反射agent 3 基于目标的agent 4 基于效用的agent 5 学习agent Exploration vs Exploitation 开篇前言 这段
  • 一文读懂什么是DHCP以及DHCP的功能特点

    随着企业中网络结构的日益复杂 接入终端的急剧增加 传统的IP分配方式已经无法满足日常工作需求 DHCP的出现有效解决了IP地址分配难题 本文中科三方针对什么是DHCP以及DHCP的功能特点做下介绍 什么是DHCP DHCP 全称Dynami
  • Python+Excel筛选未提交人员

    起因 学校给了表格让我们班长统计信息 可以用腾讯大大的TIM协作办公 让大家自己填 感觉方便了很多 然而 信息一旦变多而且顺序又没有固定 到后期想要知道未填信息的同学 这就难受了 只能找到班级名单 一个一个对照着找出未提交人员 日后此类事情
  • OCR入门教程系列(一):OCR基础导论

    作者简介 CSDN 阿里云人工智能领域博客专家 新星计划计算机视觉导师 百度飞桨PPDE 专注大数据与AI知识分享 公众号 GoAI的学习小屋 免费分享书籍 简历 导图等 更有交流群分享宝藏资料 关注公众号回复 加群 或 链接 加群 专栏推
  • 【LVGL学习笔记】image图像相关接口

    数据结构如下 Data of image typedef struct lv obj t obj const void src 图像源 指向数组 文件或符号的指针 lv point t offset lv coord t w 宽度 lv c
  • 计算机硬盘模式,硬盘三种模式的含义-电脑自学网

    硬盘三种模式的含义 NORMAL 普通模式 是最早的IDE方式 以此方式访问BIOS和IDE控制器对参数不做任何转换 该模式支持的最大柱面数为1024 最大磁头数为16 最大扇区为63 每个扇区字节数为512 因此这种模式所支持的硬盘最大容
  • Open3D点云处理算法最全合集

    Open3D点云处理算法最全合集 致力于搜集可运行 可视化较好的Open3D算法 持续更新中 1 Open3D 点云读取及可视化 离群点去除 2 Open3D 点云体素格下采样 3 Open3D 点云KdTree建立 3种近邻搜索及结果可视
  • 系统开发与运行

    系统开发与运行 系统分析与设计 需求分析 需求工程 结构化分析与设计 测试基础知识 系统运行与维护 软件架构介绍 系统分析概述 系统分析是一种问题求解技术 它将一个系统分解成各个组成部分 目的是研究各个部分如何工作 交互 以实现其系统目标
  • set_new_handler(0)是什么意思?有什么用?

    出自 STL源码剖析 第45页中有一行代码set new handler 0 源代码 inline T allocate ptrdiff t size T std set new handler 0 T tmp T operator new
  • C#中File FileInfo 和Directory DirectoryInfo 类的区别

    老师在讲C 文件操作的时候讲的个类 功能类似 但用法有区别 他们都存在于systerm IO命名空间下 File和Directory的方法都是静态方法 FileInfo和DirectoryInfo的方法都是普通方法 老师建议是如果你要在某个
  • Python之PyAudio使用

    PyAudio 播放 录音 回放 回调方法播放 非阻塞回调 PyAudio 使用这个可以进行录音 播放 生成wav文件等等 播放 coding utf 8 引入库 import pyaudio import wave import sys
  • aiohttp 异步http请求-12.aiohttp 请求生命周期(和requests库有什么不一样?)

    前言 aiohttp 请求生命周期对比requests库使用的区别 aiohttp 客户端 API 当你第一次使用 aiohttp 时 你会注意到一个简单的 HTTP 请求不是一次执行的 而是最多三个步骤 async with aiohtt
  • dac0832三角波c语言程序,单片机控制DAC0832输出正弦波三角波汇编程序

    org 0000h LJMP MAIN ORG 0003H LJMP L0 MAIN MOV R2 0aH 调幅倍数 MOV R4 01H 增减选择 MOV R5 01H pp SETB EA SETB EX0 延时计数个数 MOV A 0
  • 渗透系列之排序处产生的SQL注入问题

    1 在日常的测试过程中 发现系统的排序功能最容易产生sql注入 我们需要重点关注业务系统的排序功能 一般功能点对应这类字段 sortField sortOrder order orderby等 举个排序的SQL注入案例 发现系统的一个查询功
  • 2017-2018-1 20155227 《信息安全系统设计基础》第十三周学习总结

    2017 2018 1 20155227 信息安全系统设计基础 第十三周学习总结 找出全书你认为最重要的一章 深入重新学习一下 要求 期末占10分 完成这一章所有习题 详细总结本章要点 给你的结对学习搭档讲解你的总结并获取反馈 我选择教材第
  • 冯乐乐之六,基础光照模型

    需要理清的概念 兰伯特 半兰伯特 冯模型 布林冯模型 高光反射specular 代表物体表面反射光线 漫反射diffuse 代表吸收然后散射出表面的光线 环境光ambient 自发光emissive 漫反射 漫反射从任何方向看都是一致的 漫
  • 科学推理~

    科学推理 物理 1 力学 重力 重力并不是指向地心的 只有赤道可以 弹力 重点 判断弹力方向 相互作用力 摩擦力 静摩擦力 滑动摩擦力 注意 最大静摩擦力默认等于滑动摩擦力 压强 固体压强 液体压强 连通器 气体压强 气体对外做功 T 下降
  • 计算机图形学OpenGLC++实现: 橡皮筋技术实现折线和矩形的鼠标实现(附源码)

    废话不多说 直接开始 下列是会使用到的函数简单介绍 初始化背景 void Initial void 改变窗口大小 void ChangeSize int w int h 菜单响应函数 chooseWay int value 清除当前已经画的
  • Vue3 学习笔记 —— (一)深入理解组合式 API

    Vue3 学习笔记梳理 Vue3 学习 零 Vue3 0 与 Vue2 x 的性能对比 一 搭建环境 二 创建项目 三 Vue3 Composition API 3 1 ref or setup reactive 3 1 1 非响应式数据显
  • 北斗系统学习—JT808协议用C语言解析

    前言 本人从事于Linux应用开发 音视频应用方向 现在主要是负者AI摄像头的开发 在学音视频的途中 虽然是个小白 但是更愿意把自己所学音视频的一些知识分享给大家 以后每周都会更新哦 本期介绍的是用c语言对Jt808协议解析 要对协议进行解