包类(处理网络传输中的数据包)
这段代码定义了一个名为 CPacket
的类,用于表示和操作网络数据包。这个类的设计旨在简化封包和解包操作,使得数据包在网络传输中更易于处理。下面是对代码的详细解释:
1. 成员变量
WORD sHead; // 包头,固定值 0xFEFF
DWORD nLength; // 包的长度,包括命令、数据和校验和
WORD sCmd; // 控制命令,用于标识数据包的类型或功能
std::string strData; // 包的数据部分,可能包含可变长度的数据
WORD sSum; // 校验和,用于验证数据包的完整性
std::string strOut;// 完整的包数据,包含包头、长度、命令、数据和校验和
- sHead: 包头,固定为 0xFEFF,用于标识数据包的开始。
- nLength: 表示数据包的长度,从命令开始到校验和结束的总长度。
- sCmd: 控制命令,用于标识包的类型或执行的操作。
- strData: 数据包的主体部分,可以包含任意的二进制数据。
- sSum: 校验和,用于数据完整性的验证。通过将数据包的所有字节相加(模 0xFFFF)得到。
- strOut: 整个包的数据,包括所有部分,作为一个完整的二进制序列。
2. 构造函数
默认构造函数
CPacket() :sHead(0), nLength(0), sCmd(0), sSum(0) {}
- 默认构造函数:初始化所有成员变量为零。这使得创建一个空的
CPacket
对象。
参数化构造函数
CPacket(WORD nCmd, const BYTE* pData, size_t nSize) {
sHead = 0xFEFF;
nLength = nSize + 4;
sCmd = nCmd;
if (nSize > 0) {
strData.resize(nSize);
memcpy((void*)strData.c_str(), pData, nSize);
} else {
strData.clear();
}
sSum = 0;
for (size_t j = 0; j < strData.size(); j++) {
sSum += BYTE(strData[j]) & 0xFF;
}
}
- 参数化构造函数:接受命令 (
nCmd
)、数据 (pData
)、数据大小 (nSize
) 作为参数,构建一个包含这些信息的数据包。 - 设置包头:
sHead
被设置为固定值 0xFEFF。 - 计算并设置包长度:
nLength
为数据大小加上命令和校验和的长度(共 4 字节)。 - 拷贝数据:如果数据存在(
nSize > 0
),则将其拷贝到strData
中;否则,清空strData
。 - 计算校验和:通过累加
strData
中每个字节的值来计算sSum
。
拷贝构造函数
CPacket(const CPacket& pack) {
sHead = pack.sHead;
nLength = pack.nLength;
sCmd = pack.sCmd;
strData = pack.strData;
sSum = pack.sSum;
}
- 拷贝构造函数:通过复制另一个
CPacket
对象的所有成员变量来构造新对象。
解包构造函数
CPacket(const BYTE* pData, size_t& nSize) {
size_t i = 0;
for (; i < nSize; i++) {
if (*(WORD*)(pData + i) == 0xFEFF) {
sHead = *(WORD*)(pData + i);
i += 2;
break;
}
}
if (i + 4 + 2 + 2 > nSize) {
nSize = 0;
return;
}
nLength = *(DWORD*)(pData + i); i += 4;
if (nLength + i > nSize) {
nSize = 0;
return;
}
sCmd = *(WORD*)(pData + i); i += 2;
if (nLength > 4) {
strData.resize(nLength - 2 - 2);
memcpy((void*)strData.c_str(), pData + i, nLength - 4);
i += nLength - 4;
}
sSum = *(WORD*)(pData + i); i += 2;
WORD sum = 0;
for (size_t j = 0; j < strData.size(); j++) {
sum += BYTE(strData[j]) & 0xFF;
}
if (sum == sSum) {
nSize = i;
return;
}
nSize = 0;
}
- 解包构造函数:从一个原始字节数组(
pData
)中解析出数据包,如果解析成功,更新nSize
以反映解析的字节数。 - 找到包头:在数据中查找
0xFEFF
,找到后将sHead
设置为该值。 - 检查数据完整性:通过检查长度、命令、数据和校验和,确保数据包完整。
- 验证校验和:计算
strData
的校验和,并与接收到的sSum
进行比较,若匹配则认为数据包有效。
3. 析构函数
~CPacket() {}
- 析构函数:没有执行特别的清理工作,因为使用的主要是自动管理内存的
std::string
。
4. 运算符重载
CPacket& operator=(const CPacket& pack) {
if (this != &pack) {
sHead = pack.sHead;
nLength = pack.nLength;
sCmd = pack.sCmd;
strData = pack.strData;
sSum = pack.sSum;
}
return *this;
}
- 赋值运算符:重载赋值运算符,使得可以将一个
CPacket
对象赋值给另一个对象。通过检查自赋值,确保安全。
5. 辅助方法
Size()
方法
int Size() {
return nLength + 6;
}
- 计算包的大小:返回包的总长度,包括包头(2字节)、长度字段(4字节)、命令、数据和校验和。
Data()
方法
const char* Data() {
strOut.resize(nLength + 6);
BYTE* pData = (BYTE*)strOut.c_str();
*(WORD*)pData = sHead; pData += 2;
*(DWORD*)(pData) = nLength; pData += 4;
*(WORD*)pData = sCmd; pData += 2;
memcpy(pData, strData.c_str(), strData.size()); pData += strData.size();
*(WORD*)pData = sSum;
return strOut.c_str();
}
- 获取完整包数据:返回包含整个包数据的指针(
char*
类型),可直接用于发送或其他处理。这个方法将数据打包成完整的数据包格式,并存储在strOut
中。
6. 关联逻辑
- 封包与解包:
CPacket
类的设计目的是处理网络传输中的数据包。它提供了封装(CPacket(WORD nCmd, const BYTE* pData, size_t nSize)
)和解包(CPacket(const BYTE* pData, size_t& nSize)
)的功能,确保数据包在传输中能够正确构建和解析。 - 数据校验:通过校验和(
sSum
)确保数据包的完整性,如果校验失败,解包过程将中止。 - 数据安全性:类中所有动态数据分配和拷贝操作都通过
std::string
进行管理,避免了手动内存管理的复杂性。
总之,CPacket
类是一个面向网络通信的数据包管理工具,它通过简单的接口和严谨的数据结构,保证了数据包的可靠传输和解析。
class CPacket
{
public:
CPacket() :sHead(0), nLength(0), sCmd(0), sSum(0) {}
CPacket(WORD nCmd, const BYTE* pData, size_t nSize) {
sHead = 0xFEFF;
nLength = nSize + 4;
sCmd = nCmd;
if (nSize > 0) {
strData.resize(nSize);
memcpy((void*)strData.c_str(), pData, nSize);
}
else {
strData.clear();
}
sSum = 0;
for (size_t j = 0; j < strData.size(); j++)
{
sSum += BYTE(strData[j]) & 0xFF;
}
}
CPacket(const CPacket& pack) {
sHead = pack.sHead;
nLength = pack.nLength;
sCmd = pack.sCmd;
strData = pack.strData;
sSum = pack.sSum;
}
CPacket(const BYTE* pData, size_t& nSize) {
size_t i = 0;
for (; i < nSize; i++) {
if (*(WORD*)(pData + i) == 0xFEFF) {
sHead = *(WORD*)(pData + i);
i += 2;
break;
}
}
if (i + 4 + 2 + 2 > nSize) {//包数据可能不全,或者包头未能全部接收到
nSize = 0;
return;
}
nLength = *(DWORD*)(pData + i); i += 4;
if (nLength + i > nSize) {//包未完全接收到,就返回,解析失败
nSize = 0;
return;
}
sCmd = *(WORD*)(pData + i); i += 2;
if (nLength > 4) {
strData.resize(nLength - 2 - 2);
memcpy((void*)strData.c_str(), pData + i, nLength - 4);
i += nLength - 4;
}
sSum = *(WORD*)(pData + i); i += 2;
WORD sum = 0;
for (size_t j = 0; j < strData.size(); j++)
{
sum += BYTE(strData[j]) & 0xFF;
}
if (sum == sSum) {
nSize = i;//head2 length4 data...
return;
}
nSize = 0;
}
~CPacket() {}
CPacket& operator=(const CPacket& pack) {
if (this != &pack) {
sHead = pack.sHead;
nLength = pack.nLength;
sCmd = pack.sCmd;
strData = pack.strData;
sSum = pack.sSum;
}
return *this;
}
int Size() {//包数据的大小
return nLength + 6;
}
const char* Data() {
strOut.resize(nLength + 6);
BYTE* pData = (BYTE*)strOut.c_str();
*(WORD*)pData = sHead; pData += 2;
*(DWORD*)(pData) = nLength; pData += 4;
*(WORD*)pData = sCmd; pData += 2;
memcpy(pData, strData.c_str(), strData.size()); pData += strData.size();
*(WORD*)pData = sSum;
return strOut.c_str();
}
public:
WORD sHead;//固定位 0xFEFF
DWORD nLength;//包长度(从控制命令开始,到和校验结束)
WORD sCmd;//控制命令
std::string strData;//包数据
WORD sSum;//和校验
std::string strOut;//整个包的数据
};