欢迎来到厦门皓佑物联科技有限公司官方网站!
您的位置: 首页 - 新闻资讯 - 在物联网应用中需要经常处理数据帧,请你写一段处理数据帧的代码将收到的数据进行解析输出

在物联网应用中需要经常处理数据帧,请你写一段处理数据帧的代码将收到的数据进行解析输出

来源:新闻资讯 / 时间: 2024-11-22

提示: 1、数据帧的长度不定,但是帧头帧尾是固定的 2、数据帧的参数数量不定,请注意 3、每次收到的数据可能不是完整的一帧,但是不能把不完整的数据帧丢弃,应该等待到下一完整帧接收到后才丢弃 4、一次可能接受到不止一个数据帧,可能是多个,需要针对不同数据帧进行分割

搜说关键词:数据帧粘包 数据帧拆包 帧处理

以下内容文献摘抄CSDN博客的bandaoyu博主,加上自己的一些总结:

什么是分包与拆包呢?
       假设客户端分别发送了两个数据包D1和D2给服务端,由于服务端一次读取到的字节数是不确定的,故可能存在以下4种情况:
       (1)服务端分两次读取到了两个独立的数据包,分别是D1和D2,没有粘包和拆包;
       (2)服务端一次接收到了两个数据包,D1和D2粘合在一起,被称为TCP粘包;
       (3)服务端分两次读取到了两个数据包,第一次读取到了完整的D1包和D2包的部分内容,第二次读取到了D2包的剩余内容,这被称为TCP拆包;
       (4)服务端分两次读取到了两个数据包,第一次读取到了D1包的部分内容D1_1,第二次读取到了D1包的剩余内容D1_2和D2包的整包。

粘包和拆包原因
(1)要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包;
(2)接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包;
(3)要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包;
(4)待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。即TCP报文长度-TCP头部长度>MSS。

如何识别帧头帧尾

在单片机上有2种方式。

方式1:利用2帧数据报文间隔时间来确定一帧数据包。如果在其后连续3.5字节时间内都没有收到一个字节,则认为之前收到的
报文,就是一条完整的报文。假定收到该报文长度为10,则接收缓冲区数组[0]=帧头,接收缓冲区数组[9]=帧尾。
方式2:利用特殊字符串1作为帧头,特殊字符串2作为帧尾。而且特殊字符串1不允许出现在报文中,只能作为帧头;特殊字符串2不允许出现在报文中,只能作为帧尾。回车+换行符不可能出现在正常报文中,只能用于帧尾。
方式3:帧头+帧长度

缓存解包的方式识别帧

1、缓存解包

单片机上普通采用环形缓冲区来解决缓存解包问题。
     所谓缓存解包就是建立一个大的环形缓冲区,环形缓冲区的长度至少是【最长报文】的n倍。环形缓冲区有独立的写指针和读指针,因此就算出现粘包,也不会影响解析报文工作。

2、拆包
   假设客户端分别发送了两个数据包D1和D2给服务端,由于服务端一次读取到的字节数是不确定的,故可能存在以下4种情况。
       (1)服务端分两次读取到了两个独立的数据包,分别是D1和D2,【报文是完整的】,没有粘包和拆包;
       (2)服务端一次接收到了两个数据包,D1和D2粘合在一起,被称为TCP粘包;
       (3)服务端分两次读取到了两个数据包,第一次读取到了完整的D1包和D2包的部分内容,第二次读取到了D2包的剩余内容,这被称为TCP拆包;
       (4)服务端分两次读取到了两个数据包,第一次读取到了D1包的部分内容D1_1,第二次读取到了D1包的剩余内容D1_2和D2包的整包
        上述(2)、(3)、(4)种情况,就是拆包。上述(1)情况不需要拆包,因为D1和D2分别都是独自完整的包。
下一是法一,但是有错误

#include<iostream>
#include<vector>
using namespace std;

//定义数据帧结构体
struct DataFrame
{
	//假设数据帧由4个字节的数据和1个字节的校验码组成
	vector<unsigned char>data;
	unsigned char checksum;
};

//数据帧解析函数
void pareaDataFrame(const vector<unsigned char >& receiveData)
{
	if (receiveData.size() != sizeof(DataFrame))
	{
		std::cout << "接收的数据不是有效的数据框" << endl;
		return;
	}

}

//将接受到的数据进行类型转换,以便访问各个字段
const DataFrame* frame = reinterpret_cast<const DataFrame*>(receivedData.data());

std::cout << "接收的数据框" << endl;
std::cout << "Data:"; // 这里缺少打印data字段的代码
std::cout << "checksum:" << static_cast<int>(frame->checksum) << endl;


int main()
{
	//假设受到的数据帧保存在receiveData变量中
	vector<unsigned char>receiveData = { 0x01,0x02,0x03,0x04,0xAB };
	pareaDataFrame(receiveData);

	return 0;
}

一下是法二:
上述代码是一个用于处理数据帧的函数processDataFrame和主函数main。其中,processDataFrame函数根据传入的接收到的数据receivedData,查找并处理数据帧。

在主函数main中,定义了一个接收到的数据receivedData,其中包含了多个数据帧以及部分不完整的数据帧。

processDataFrame函数首先声明一个静态变量isIncompleteFrame用于标记是否存在不完整的数据帧。然后通过查找字符串"<frame>"获取第一个数据帧的起始位置frameStart,如果未找到帧头并且上一帧数据还未完整接收到,则直接返回。

之后使用while循环处理多个数据帧的情况,首先查找帧尾的位置frameEnd,如果未找到帧尾则表示接收到的是不完整的数据帧,将isIncompleteFrame设为true,并退出循环。否则,取出完整的数据帧frame,并解析输出数据帧的内容。

继续查找下一个帧头的位置frameStart,直到所有数据帧都被处理完。

最后,如果存在不完整的数据帧且已经处理完所有数据帧,则将剩余数据保存到receivedData以便下次拼接。

主函数main中调用processDataFrame函数,并传入接收到的数据receivedData。

整体来说,该代码实现了处理数据帧的功能,可以识别并处理完整的数据帧,以及保存并等待下次拼接的不完整的数据帧。
```cpp
#include <iostream>
#include <string>
using namespace std;

void processDataFrames(const std::string& receivedData)
{
    static bool isIncompleteFrame = false; // 标记是否存在不完整的数据帧

    // 查找帧头的位置
    size_t frameStart = receivedData.find("<frame>");

    // 如果未找到帧头,表示收到的数据不完整,与上次接收到的数据拼接起来
    if (frameStart == std::string::npos && !isIncompleteFrame) 
    {
        // 上一帧数据还未完整接收到,丢弃这部分数据
        return;
    }

    // 处理多个数据帧的情况
    while (frameStart != std::string::npos) 
    {
        // 查找帧尾的位置
        size_t frameEnd = receivedData.find("</frame>", frameStart);

        // 如果未找到帧尾,表示接收到的是不完整的数据帧
        if (frameEnd == std::string::npos)
        {
            isIncompleteFrame = true;
            break;
        }

        // 取出完整的数据帧
        std::string frame = receivedData.substr(frameStart, frameEnd - frameStart + 8);

        // 解析输出数据帧的内容
        std::cout << "Received data frame: " << frame << std::endl;

        // 继续查找下一个帧头的位置
        frameStart = receivedData.find("<frame>", frameEnd);
    }

    // 如果存在不完整的数据帧,将其保存以便下次拼接
    if (isIncompleteFrame && frameStart == std::string::npos)
    {
        receivedData == receivedData.substr(frameStart);
    }
}

int main() 
{
    std::string receivedData = "<frame>data1</frame><frame>data2</frame><partial_frame/>";

    processDataFrames(receivedData);

    return 0;
}

相关产品

在线客服
微信联系
客服
扫码加微信(手机同号)
电话咨询
返回顶部