具有使用 UDP 套接字字节的限制的标头
Header with restrictions using bytes for an UDP Socket
我正在为一个UDP套接字做一个标头,该套接字对字节有限制。
|数据包 ID(1 字节(|数据包大小(2 字节(|子数据包 ID(1 字节(|等
我做了一个结构来存储这种属性,例如:
typedef struct WHEATHER_STRUCT
{
unsigned char packetID[1];
unsigned char packetSize[2];
unsigned char subPacketID[1];
unsigned char subPacketOffset[2];
...
} wheather_struct;
我使用 new 初始化了这个结构并更新了值。问题是关于我是否只想在数据包大小属性中使用 2 个字节。我在下面写的这两种形式中哪一种是正确的?
*weather_struct->packetSize = '50';
或
*weather_struct->packetSize = 50;
如果你可以使用 C++11 和 gcc(或 clang(,那么我会这样做:
typedef struct WHEATHER_STRUCT
{
uint8_t packetID;
uint16_t packetSize;
uint8_t subPacketID;
uint16_t subPacketOffset;
// ...
} __attribute__((packed)) wheather_struct;
如果您不能使用 C++11,则可以改用unsigned char
和unsigned short
。
如果你使用的是Visual C,那么你可以做:
#pragma pack (push, 1)
typedef struct ...
#pragma (pop)
还要注意字节排序问题,具体取决于您需要支持的体系结构。 您可以使用htons()
和ntohs()
来解决此问题。
万德盒现场演示
从IP数据包中打包和解压缩数据是一个与互联网本身一样古老的问题(实际上,更老(。
不同的计算机体系结构具有用于表示整数的不同布局,这可能会导致计算机之间的通信出现问题。
出于这个原因,IP 堆栈标准化了按"网络字节顺序"(这基本上意味着最高有效字节优先(编码整数。
存在将网络字节顺序的值转换为本机类型的标准函数,反之亦然。我敦促您考虑使用这些代码,因为您的代码将更具可移植性。
此外,从程序的角度抽象数据表示是有意义的。 C++ 编译器可以非常有效地执行转换。
例:
#include <arpa/inet.h>
#include <cstring>
#include <cstdint>
typedef struct WEATHER_STRUCT
{
std::int8_t packetID;
std::uint16_t packetSize;
std::uint8_t subPacketID;
std::uint16_t subPacketOffset;
} weather_struct;
const std::int8_t* populate(weather_struct& target, const std::int8_t* source)
{
auto get16 = [&source]
{
std::uint16_t buf16;
std::memcpy(&buf16, source, 2);
source += 2;
return ntohs(buf16);
};
target.packetID = *source++;
target.packetSize = get16();
target.subPacketID = *source++;
target.subPacketOffset = get16();
return source;
}
uint8_t* serialise(uint8_t* target, weather_struct const& source)
{
auto write16 = [&target](std::uint16_t val)
{
val = ntohs(val);
std::memcpy(target, &val, 2);
target += 2;
};
*target++ = source.packetID;
write16(source.packetSize);
*target++ = source.subPacketID;
write16(source.subPacketOffset);
return target;
}
https://linux.die.net/man/3/htons
以下是上述 C++17 版本的链接:
https://godbolt.org/z/oRASjI
关于转换成本的进一步说明:
传入或离开程序的数据是每个有效负载发生一次的事件。在这里遭受转换成本会产生可以忽略不计的罚款。
一旦数据到达程序,或者在数据离开之前,代码可能会对其进行多次操作。
如果数据未在自然字边界上对齐,则某些处理器体系结构在数据访问期间会遭受巨大的性能损失。这就是packed
等属性存在的原因 - 编译器正在尽其所能避免未对齐的数据。使用 pack 属性无异于故意告诉编译器生成非常不理想的代码。
出于这个原因,我建议不要使用填充结构(例如__attribute__((packed))
等(表示将由程序逻辑引用的数据。
与RAM相比,网络要慢许多数量级。与实际传输网络数据包的成本相比,在编码或解码网络数据包时微小的性能命中(字面意思是纳秒(是无关紧要的。
打包结构会导致程序代码中出现可怕的性能问题,并且通常会导致可移植性问题。
两者都不正确,您需要将两个字节视为单个 16 位数字。您可能还需要考虑网络流与处理器体系结构的不同字节序(取决于协议,大多数是大端序(。
因此,正确的代码是:
*((uint16_t*)weather_struct->packetSize) = htons(50);
如果packetSize
uint16_t
开始会更简单:
weather_struct->packetSize = htons(50);
- 具有使用 UDP 套接字字节的限制的标头
- UDP 套接字读取最后一个传入字节
- Qt TCP 套接字 - 写入超过 15 个字节
- 为什么 sys 套接字 recv 函数不填充数据但返回字节长度?
- C++,在阻塞模式下从套接字读取所有可用字节的最佳方法
- 从套接字到字节数组的读取数据中的意外字符
- C 套接字仅发送第4个字节的数据
- C++套接字不能接收超过 21845 字节
- 根据C++代码在 PHP 中创建字节数据并将其传递给套接字
- 发送的字节不等于C 套接字PRG中的接收字节
- 增强ASIO TCP套接字可用报告不正确的字节数
- Qt套接字未等待写入字节
- recv套接字中的前几个字节,以确定缓冲区大小
- boost::asio 从套接字读取 n 个字节到 Streambuf
- 通过套接字发送字节*
- 套接字 - Java 未以正确的编码接收字节
- 通过套接字从Linux向Windows机器发送空字节-会是一样的吗
- 如何在linux上获得异步套接字上可读取的字节数
- ZeroMQ-使用REQ套接字发送超过30个字节
- 套接字recv(),每次一个字节