在C/ c++中创建和发送数据包

Creating and sending data packets in C/C++

本文关键字:数据包 创建 c++      更新时间:2023-10-16

假设我想使用C或c++将以下数据发送到套接字,所有数据都在一个数据包中:

Headers
-------
Field 1: 2 byte hex
Field 2: 2 byte hex
Field 3: 4 byte hex
Data
----
Field1 : 2 byte hex
Field1 : 8 byte hex
创建和发送包含所有这些数据的数据包的代码通常是什么样子的?

让我们假设你的程序已经组织好了,头文件在一个struct中,数据在另一个struct中。例如,您可能有这些数据结构:

#include <stdint.h>
struct header {
    uint16_t f1;
    uint16_t f2;
    uint32_t f3;
};
struct data {
    uint16_t pf1;
    uint64_t pf2;
};

我们称这种组织为"主机格式"。对我来说,宿主格式是什么并不重要,只要它对程序的其余部分有用就行。我们将传递给send()的格式称为"网络格式"。(我选择这些名称是为了匹配htons(主机到网络短)和htonl(主机到网络长)名称。)

下面是一些我们可能会觉得很方便的转换函数。它们都将您的主机格式结构转换为网络格式缓冲区。

#include <arpa/inet.h>
#include <string.h>
void htonHeader(struct header h, char buffer[8]) {
    uint16_t u16;
    uint32_t u32;
    u16 = htons(h.f1);
    memcpy(buffer+0, &u16, 2);
    u16 = htons(h.f2);
    memcpy(buffer+2, &u16, 2);
    u32 = htonl(h.f3);
    memcpy(buffer+4, &u32, 4);
}
void htonData(struct data d, char buffer[10]) {
    uint16_t u16;
    uint32_t u32;
    u16 = htons(d.pf1);
    memcpy(buffer+0, &u16, 2);
    u32 = htonl(d.pf2>>32);
    memcpy(buffer+2, &u32, 4);
    u32 = htonl(d.pf2);
    memcpy(buffer+6, u32, 4);
}
void htonHeaderData(struct header h, struct data d, char buffer[18]) {
    htonHeader(h, buffer+0);
    htonData(d, buffer+8);
}

发送你的数据,这样做:

...
char buffer[18];
htonHeaderData(myPacketHeader, myPacketData, buffer);
send(sockfd, buffer, 18, 0);
...
同样,您不必使用我定义的headerdata结构体。只要使用你的程序需要的任何东西。关键是你有一个转换函数,它将所有的数据,在定义好的偏移量,以定义好的字节顺序,写入缓冲区,并将该缓冲区传递给send()函数。

在网络连接的另一端,您需要一个程序来解释它接收到的数据。在这方面,您需要编写相应的函数(ntohHeader等)。这些函数将memcpy缓冲区中的位放入一个局部变量中,该局部变量可以传递给ntohsntohl。我将把这些函数留给你来写。

嗯,通常情况下,它看起来像是将数据包结构准备到内存缓冲区中(明智地调用htonl函数族)。

然后使用send, sendto, sendmsgwrite函数,希望非常小心缓冲区的长度和良好的错误处理/报告。

(或用于发送的Win32 api之一,如果这是目标平台。)

你可以在Beej的网络编程指南中找到一个很好的演示。

特别是对于字节打包部分(考虑到端序),请查看序列化主题。(在这一节中,的细节远远超过了普通的固定大小的整数数据类型。

根据操作系统的网络库(*nix使用Berkeley套接字,Windows使用Winsock等),代码看起来会有所不同。但是,您可以创建一个结构体,其中包含您想要在数据包中发送的所有数据,例如

typedef struct
{
    short field1;
    short field2;
    int field3;
} HeaderStruct;
typedef struct
{
    short field1;
    long long field2;
} PacketDataStruct;

假设为32位整型。

编辑:

正如有人在评论中善意地提醒我的那样,不要忘记转换到和退出网络秩序。网络库将具有帮助完成此任务的函数,例如ntohsnothlhtonshtonl

一个简单的答案是,它将以接收方期望的格式发送。不过,这有点回避了问题。假设数据是如图所示的固定大小,并且接收端所期望的,那么您可以使用打包(1字节对齐)结构并将数据存储在每个字段中。使用1字节对齐的原因是,通常更容易确保两端期望相同的数据。如果没有1字节对齐,那么结构可能会根据编译器选项,32位与64位体系结构等而有所不同)并且,通常,如果十六进制值是整数,则预计您将以网络字节顺序发送值。您可以使用htonshtonl(如果可用,可能还有htobe64)等函数来转换它们。

假设数据在具有所需字节顺序的结构中,那么send调用可能是这样的:

ret = send( socket, &mystruct, sizeof( mystruct ), 0 );

假定将mystruct声明为结构体的实例,而不是指向结构体的指针。