使用C / C ++创建和发送数据包

时间:2011-04-26 19:12:54

标签: c++ c sockets

假设我想将以下数据发送到使用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

创建和发送包含所有这些数据的数据包的代码通常是什么样的?

4 个答案:

答案 0 :(得分:17)

我们假设您的程序已经整理好,标题在一个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。我会把这些功能留给你写。

答案 1 :(得分:5)

好吧,通常看起来它正在准备将数据包结构放入内存缓冲区(明智地调用htonl函数系列)。

如果然后使用sendsendtosendmsgwrite函数,希望在缓冲区的长度和良好的错误处理方面花费很多精力/报告。

(如果是目标平板,则为发送的Win32 api之一。)

您可以在Beej's Guide to Network Programming找到关于所有这些内容的精彩演示文稿。

特别是对于字节打包部分(使用endian考虑),请查看serialization主题。 (该部分中的方式更多详细信息比纯固定大小整数数据类型所需的更多

答案 2 :(得分:2)

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

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

typedef struct
{
    short field1;
    long long field2;
} PacketDataStruct;

假设一个32位的int大小。

修改

正如有人在评论中提醒我的那样,不要忘记转换来往Network Order。网络库将具有协助解决此问题的功能,例如ntohsnothlhtonshtonl

答案 3 :(得分:1)

一个简单的答案是它将以接收者期望的格式发送。不过,这引起了一些问题。假设数据是如图所示的固定大小且接收端需要,那么您可以使用压缩(1字节对齐)结构并将数据存储在每个字段中。使用1字节对齐的原因是通常更容易确保两端都期望相同的数据。如果没有1字节对齐,那么结构可能会根据编译器选项,32位与64位架构等看起来不同。)通常,如果十六进制值,您应该以网络字节顺序发送值是整数。您可以使用htonshtonl(以及可能的htobe64等功能)进行转换。

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

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

假设mystruct被声明为结构的实例,而不是指向结构的指针。