Files
SimpleRemoter/test/unit/network/PacketFragmentTest.cpp
2026-04-19 22:55:21 +02:00

661 lines
19 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file PacketFragmentTest.cpp
* @brief 粘包/分包处理测试
*
* 测试覆盖:
* - 完整包接收和解析
* - 分包(不完整包)处理
* - 粘包(多个包粘在一起)处理
* - 混合场景测试
* - 边界条件处理
*/
#include <gtest/gtest.h>
#include <cstring>
#include <cstdint>
#include <vector>
#include <queue>
#include <functional>
// ============================================
// 协议常量
// ============================================
const int FLAG_LENGTH = 8;
const int HDR_LENGTH = 16; // FLAG(8) + PackedLen(4) + OrigLen(4)
// ============================================
// 简化版 CBuffer测试专用
// ============================================
class TestBuffer {
public:
TestBuffer() {}
void WriteBuffer(const uint8_t* data, size_t len) {
m_data.insert(m_data.end(), data, data + len);
}
size_t ReadBuffer(uint8_t* dst, size_t len) {
size_t toRead = std::min(len, m_data.size());
if (toRead > 0) {
memcpy(dst, m_data.data(), toRead);
m_data.erase(m_data.begin(), m_data.begin() + toRead);
}
return toRead;
}
void Skip(size_t len) {
size_t toSkip = std::min(len, m_data.size());
m_data.erase(m_data.begin(), m_data.begin() + toSkip);
}
size_t GetBufferLength() const {
return m_data.size();
}
const uint8_t* GetBuffer(size_t pos = 0) const {
if (pos >= m_data.size()) return nullptr;
return m_data.data() + pos;
}
bool CopyBuffer(uint8_t* dst, size_t pos, size_t len) const {
if (pos + len > m_data.size()) return false;
memcpy(dst, m_data.data() + pos, len);
return true;
}
void Clear() {
m_data.clear();
}
private:
std::vector<uint8_t> m_data;
};
// ============================================
// 数据包构建辅助函数
// ============================================
#pragma pack(push, 1)
struct PacketHeader {
char flag[FLAG_LENGTH];
uint32_t packedLength; // 包含头部的总长度
uint32_t originalLength; // 原始数据长度
};
#pragma pack(pop)
std::vector<uint8_t> BuildPacket(const std::vector<uint8_t>& payload, uint8_t key = 0x42) {
std::vector<uint8_t> packet(HDR_LENGTH + payload.size());
PacketHeader* hdr = reinterpret_cast<PacketHeader*>(packet.data());
memcpy(hdr->flag, "HELL", 4);
hdr->flag[6] = key;
hdr->flag[7] = ~key;
hdr->packedLength = static_cast<uint32_t>(HDR_LENGTH + payload.size());
hdr->originalLength = static_cast<uint32_t>(payload.size());
if (!payload.empty()) {
memcpy(packet.data() + HDR_LENGTH, payload.data(), payload.size());
}
return packet;
}
std::vector<uint8_t> BuildPacketWithData(size_t dataSize, uint8_t fillByte = 0xAA) {
std::vector<uint8_t> payload(dataSize, fillByte);
return BuildPacket(payload);
}
// ============================================
// 粘包/分包处理器(模拟 OnServerReceiving 逻辑)
// ============================================
class PacketProcessor {
public:
using PacketCallback = std::function<void(const std::vector<uint8_t>&)>;
PacketProcessor(PacketCallback callback) : m_callback(callback) {}
// 接收数据(模拟网络接收)
void OnReceive(const uint8_t* data, size_t len) {
m_buffer.WriteBuffer(data, len);
ProcessBuffer();
}
// 获取待处理的字节数
size_t GetPendingBytes() const {
return m_buffer.GetBufferLength();
}
// 获取已处理的包数量
size_t GetProcessedCount() const {
return m_processedCount;
}
private:
void ProcessBuffer() {
while (m_buffer.GetBufferLength() >= HDR_LENGTH) {
// 验证头部
const uint8_t* buf = m_buffer.GetBuffer();
if (memcmp(buf, "HELL", 4) != 0) {
// 无效头部,跳过一个字节重试
m_buffer.Skip(1);
continue;
}
// 读取包长度
uint32_t packedLength;
m_buffer.CopyBuffer(reinterpret_cast<uint8_t*>(&packedLength),
FLAG_LENGTH, sizeof(uint32_t));
// 检查长度有效性
if (packedLength < HDR_LENGTH || packedLength > 100 * 1024 * 1024) {
// 无效长度,跳过头部
m_buffer.Skip(FLAG_LENGTH);
continue;
}
// 检查包是否完整
if (m_buffer.GetBufferLength() < packedLength) {
// 不完整,等待更多数据
break;
}
// 读取完整包
std::vector<uint8_t> packet(packedLength);
m_buffer.ReadBuffer(packet.data(), packedLength);
// 提取 payload
std::vector<uint8_t> payload(packet.begin() + HDR_LENGTH, packet.end());
m_callback(payload);
m_processedCount++;
}
}
TestBuffer m_buffer;
PacketCallback m_callback;
size_t m_processedCount = 0;
};
// ============================================
// 完整包接收测试
// ============================================
class CompletePacketTest : public ::testing::Test {
protected:
std::vector<std::vector<uint8_t>> receivedPackets;
void SetUp() override {
receivedPackets.clear();
}
PacketProcessor::PacketCallback GetCallback() {
return [this](const std::vector<uint8_t>& payload) {
receivedPackets.push_back(payload);
};
}
};
TEST_F(CompletePacketTest, SinglePacket) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload = {0x01, 0x02, 0x03, 0x04};
auto packet = BuildPacket(payload);
processor.OnReceive(packet.data(), packet.size());
ASSERT_EQ(receivedPackets.size(), 1u);
EXPECT_EQ(receivedPackets[0], payload);
EXPECT_EQ(processor.GetPendingBytes(), 0u);
}
TEST_F(CompletePacketTest, EmptyPayload) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload;
auto packet = BuildPacket(payload);
processor.OnReceive(packet.data(), packet.size());
ASSERT_EQ(receivedPackets.size(), 1u);
EXPECT_TRUE(receivedPackets[0].empty());
}
TEST_F(CompletePacketTest, LargePayload) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload(64 * 1024); // 64 KB
for (size_t i = 0; i < payload.size(); ++i) {
payload[i] = static_cast<uint8_t>(i & 0xFF);
}
auto packet = BuildPacket(payload);
processor.OnReceive(packet.data(), packet.size());
ASSERT_EQ(receivedPackets.size(), 1u);
EXPECT_EQ(receivedPackets[0], payload);
}
// ============================================
// 分包(不完整包)测试
// ============================================
class FragmentedPacketTest : public ::testing::Test {
protected:
std::vector<std::vector<uint8_t>> receivedPackets;
PacketProcessor::PacketCallback GetCallback() {
return [this](const std::vector<uint8_t>& payload) {
receivedPackets.push_back(payload);
};
}
};
TEST_F(FragmentedPacketTest, TwoFragments) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload = {0xAA, 0xBB, 0xCC, 0xDD};
auto packet = BuildPacket(payload);
// 分两次发送
size_t half = packet.size() / 2;
processor.OnReceive(packet.data(), half);
EXPECT_EQ(receivedPackets.size(), 0u);
EXPECT_EQ(processor.GetPendingBytes(), half);
processor.OnReceive(packet.data() + half, packet.size() - half);
ASSERT_EQ(receivedPackets.size(), 1u);
EXPECT_EQ(receivedPackets[0], payload);
}
TEST_F(FragmentedPacketTest, ManyFragments) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload(100);
for (size_t i = 0; i < payload.size(); ++i) {
payload[i] = static_cast<uint8_t>(i);
}
auto packet = BuildPacket(payload);
// 每次发送 10 字节
for (size_t i = 0; i < packet.size(); i += 10) {
size_t len = std::min(size_t(10), packet.size() - i);
processor.OnReceive(packet.data() + i, len);
}
ASSERT_EQ(receivedPackets.size(), 1u);
EXPECT_EQ(receivedPackets[0], payload);
}
TEST_F(FragmentedPacketTest, OnlyHeader) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload = {0x01, 0x02};
auto packet = BuildPacket(payload);
// 只发送头部
processor.OnReceive(packet.data(), HDR_LENGTH);
EXPECT_EQ(receivedPackets.size(), 0u);
EXPECT_EQ(processor.GetPendingBytes(), HDR_LENGTH);
// 发送剩余数据
processor.OnReceive(packet.data() + HDR_LENGTH, packet.size() - HDR_LENGTH);
ASSERT_EQ(receivedPackets.size(), 1u);
}
TEST_F(FragmentedPacketTest, PartialHeader) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload = {0xFF};
auto packet = BuildPacket(payload);
// 发送不完整的头部
processor.OnReceive(packet.data(), 4); // 只有 "HELL"
EXPECT_EQ(receivedPackets.size(), 0u);
// 发送剩余部分
processor.OnReceive(packet.data() + 4, packet.size() - 4);
ASSERT_EQ(receivedPackets.size(), 1u);
}
// ============================================
// 粘包测试
// ============================================
class StickyPacketTest : public ::testing::Test {
protected:
std::vector<std::vector<uint8_t>> receivedPackets;
PacketProcessor::PacketCallback GetCallback() {
return [this](const std::vector<uint8_t>& payload) {
receivedPackets.push_back(payload);
};
}
};
TEST_F(StickyPacketTest, TwoPacketsStuckTogether) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload1 = {0x11, 0x22};
std::vector<uint8_t> payload2 = {0x33, 0x44, 0x55};
auto packet1 = BuildPacket(payload1);
auto packet2 = BuildPacket(payload2);
// 合并两个包
std::vector<uint8_t> combined;
combined.insert(combined.end(), packet1.begin(), packet1.end());
combined.insert(combined.end(), packet2.begin(), packet2.end());
processor.OnReceive(combined.data(), combined.size());
ASSERT_EQ(receivedPackets.size(), 2u);
EXPECT_EQ(receivedPackets[0], payload1);
EXPECT_EQ(receivedPackets[1], payload2);
}
TEST_F(StickyPacketTest, ThreePacketsStuckTogether) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload1 = {0x01};
std::vector<uint8_t> payload2 = {0x02, 0x03};
std::vector<uint8_t> payload3 = {0x04, 0x05, 0x06};
auto packet1 = BuildPacket(payload1);
auto packet2 = BuildPacket(payload2);
auto packet3 = BuildPacket(payload3);
// 合并三个包
std::vector<uint8_t> combined;
combined.insert(combined.end(), packet1.begin(), packet1.end());
combined.insert(combined.end(), packet2.begin(), packet2.end());
combined.insert(combined.end(), packet3.begin(), packet3.end());
processor.OnReceive(combined.data(), combined.size());
ASSERT_EQ(receivedPackets.size(), 3u);
EXPECT_EQ(receivedPackets[0], payload1);
EXPECT_EQ(receivedPackets[1], payload2);
EXPECT_EQ(receivedPackets[2], payload3);
}
TEST_F(StickyPacketTest, ManyPacketsStuckTogether) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> combined;
const int numPackets = 100;
for (int i = 0; i < numPackets; ++i) {
std::vector<uint8_t> payload(i % 10 + 1, static_cast<uint8_t>(i));
auto packet = BuildPacket(payload);
combined.insert(combined.end(), packet.begin(), packet.end());
}
processor.OnReceive(combined.data(), combined.size());
EXPECT_EQ(receivedPackets.size(), numPackets);
}
// ============================================
// 混合场景测试
// ============================================
class MixedScenarioTest : public ::testing::Test {
protected:
std::vector<std::vector<uint8_t>> receivedPackets;
PacketProcessor::PacketCallback GetCallback() {
return [this](const std::vector<uint8_t>& payload) {
receivedPackets.push_back(payload);
};
}
};
TEST_F(MixedScenarioTest, OneAndHalfPackets) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload1 = {0xAA, 0xBB};
std::vector<uint8_t> payload2 = {0xCC, 0xDD, 0xEE, 0xFF};
auto packet1 = BuildPacket(payload1);
auto packet2 = BuildPacket(payload2);
// 发送完整包1 + 半个包2
std::vector<uint8_t> firstSend;
firstSend.insert(firstSend.end(), packet1.begin(), packet1.end());
firstSend.insert(firstSend.end(), packet2.begin(), packet2.begin() + packet2.size() / 2);
processor.OnReceive(firstSend.data(), firstSend.size());
EXPECT_EQ(receivedPackets.size(), 1u); // 只处理了包1
// 发送剩余的半个包2
processor.OnReceive(packet2.data() + packet2.size() / 2, packet2.size() - packet2.size() / 2);
ASSERT_EQ(receivedPackets.size(), 2u);
EXPECT_EQ(receivedPackets[0], payload1);
EXPECT_EQ(receivedPackets[1], payload2);
}
TEST_F(MixedScenarioTest, HalfPacketThenOneAndHalf) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload1 = {0x11};
std::vector<uint8_t> payload2 = {0x22, 0x33};
auto packet1 = BuildPacket(payload1);
auto packet2 = BuildPacket(payload2);
// 发送半个包1
processor.OnReceive(packet1.data(), packet1.size() / 2);
EXPECT_EQ(receivedPackets.size(), 0u);
// 发送剩余包1 + 完整包2
std::vector<uint8_t> secondSend;
secondSend.insert(secondSend.end(), packet1.begin() + packet1.size() / 2, packet1.end());
secondSend.insert(secondSend.end(), packet2.begin(), packet2.end());
processor.OnReceive(secondSend.data(), secondSend.size());
ASSERT_EQ(receivedPackets.size(), 2u);
}
TEST_F(MixedScenarioTest, RandomChunkSizes) {
PacketProcessor processor(GetCallback());
// 准备多个包
std::vector<std::vector<uint8_t>> payloads;
std::vector<uint8_t> allData;
for (int i = 0; i < 10; ++i) {
std::vector<uint8_t> payload(i * 5 + 10, static_cast<uint8_t>(i));
payloads.push_back(payload);
auto packet = BuildPacket(payload);
allData.insert(allData.end(), packet.begin(), packet.end());
}
// 使用"随机"大小的块发送
size_t chunkSizes[] = {1, 7, 15, 16, 17, 31, 32, 33, 64, 128};
size_t pos = 0;
size_t chunkIdx = 0;
while (pos < allData.size()) {
size_t chunkSize = chunkSizes[chunkIdx % (sizeof(chunkSizes) / sizeof(chunkSizes[0]))];
size_t len = std::min(chunkSize, allData.size() - pos);
processor.OnReceive(allData.data() + pos, len);
pos += len;
chunkIdx++;
}
ASSERT_EQ(receivedPackets.size(), payloads.size());
for (size_t i = 0; i < payloads.size(); ++i) {
EXPECT_EQ(receivedPackets[i], payloads[i]) << "Mismatch at packet " << i;
}
}
// ============================================
// 边界条件测试
// ============================================
class PacketBoundaryTest : public ::testing::Test {
protected:
std::vector<std::vector<uint8_t>> receivedPackets;
PacketProcessor::PacketCallback GetCallback() {
return [this](const std::vector<uint8_t>& payload) {
receivedPackets.push_back(payload);
};
}
};
TEST_F(PacketBoundaryTest, ExactlyHdrLength) {
PacketProcessor processor(GetCallback());
// 只有头部,无 payload
std::vector<uint8_t> packet(HDR_LENGTH);
PacketHeader* hdr = reinterpret_cast<PacketHeader*>(packet.data());
memcpy(hdr->flag, "HELL", 4);
hdr->flag[6] = 0x42;
hdr->flag[7] = ~0x42;
hdr->packedLength = HDR_LENGTH;
hdr->originalLength = 0;
processor.OnReceive(packet.data(), packet.size());
ASSERT_EQ(receivedPackets.size(), 1u);
EXPECT_TRUE(receivedPackets[0].empty());
}
TEST_F(PacketBoundaryTest, SingleBytePayload) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload = {0xFF};
auto packet = BuildPacket(payload);
processor.OnReceive(packet.data(), packet.size());
ASSERT_EQ(receivedPackets.size(), 1u);
EXPECT_EQ(receivedPackets[0].size(), 1u);
EXPECT_EQ(receivedPackets[0][0], 0xFF);
}
TEST_F(PacketBoundaryTest, ByteByByteReceive) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload = {0x12, 0x34, 0x56};
auto packet = BuildPacket(payload);
// 每次接收 1 字节
for (size_t i = 0; i < packet.size(); ++i) {
processor.OnReceive(packet.data() + i, 1);
}
ASSERT_EQ(receivedPackets.size(), 1u);
EXPECT_EQ(receivedPackets[0], payload);
}
// ============================================
// 数据完整性测试
// ============================================
class DataIntegrityTest : public ::testing::Test {
protected:
std::vector<std::vector<uint8_t>> receivedPackets;
PacketProcessor::PacketCallback GetCallback() {
return [this](const std::vector<uint8_t>& payload) {
receivedPackets.push_back(payload);
};
}
};
TEST_F(DataIntegrityTest, BinaryData) {
PacketProcessor processor(GetCallback());
// 包含所有字节值的 payload
std::vector<uint8_t> payload(256);
for (int i = 0; i < 256; ++i) {
payload[i] = static_cast<uint8_t>(i);
}
auto packet = BuildPacket(payload);
processor.OnReceive(packet.data(), packet.size());
ASSERT_EQ(receivedPackets.size(), 1u);
EXPECT_EQ(receivedPackets[0], payload);
}
TEST_F(DataIntegrityTest, AllZeros) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload(100, 0x00);
auto packet = BuildPacket(payload);
processor.OnReceive(packet.data(), packet.size());
ASSERT_EQ(receivedPackets.size(), 1u);
for (uint8_t b : receivedPackets[0]) {
EXPECT_EQ(b, 0x00);
}
}
TEST_F(DataIntegrityTest, AllOnes) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload(100, 0xFF);
auto packet = BuildPacket(payload);
processor.OnReceive(packet.data(), packet.size());
ASSERT_EQ(receivedPackets.size(), 1u);
for (uint8_t b : receivedPackets[0]) {
EXPECT_EQ(b, 0xFF);
}
}
// ============================================
// 性能相关测试
// ============================================
class PacketPerformanceTest : public ::testing::Test {
protected:
size_t packetCount = 0;
PacketProcessor::PacketCallback GetCallback() {
return [this](const std::vector<uint8_t>& payload) {
packetCount++;
};
}
};
TEST_F(PacketPerformanceTest, ManySmallPackets) {
PacketProcessor processor(GetCallback());
const int numPackets = 10000;
std::vector<uint8_t> allData;
for (int i = 0; i < numPackets; ++i) {
std::vector<uint8_t> payload = {static_cast<uint8_t>(i & 0xFF)};
auto packet = BuildPacket(payload);
allData.insert(allData.end(), packet.begin(), packet.end());
}
processor.OnReceive(allData.data(), allData.size());
EXPECT_EQ(packetCount, numPackets);
}
TEST_F(PacketPerformanceTest, LargePacketInSmallChunks) {
PacketProcessor processor(GetCallback());
std::vector<uint8_t> payload(100 * 1024); // 100 KB
for (size_t i = 0; i < payload.size(); ++i) {
payload[i] = static_cast<uint8_t>(i & 0xFF);
}
auto packet = BuildPacket(payload);
// 每次发送 1 KB
const size_t chunkSize = 1024;
for (size_t i = 0; i < packet.size(); i += chunkSize) {
size_t len = std::min(chunkSize, packet.size() - i);
processor.OnReceive(packet.data() + i, len);
}
EXPECT_EQ(packetCount, 1u);
}