简体   繁体   中英

Creating and sending data packets in C/C++

Let's say I want to send the following data to a socket using C or C++, all in one packet:

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

What would the code typically look like to create and send the packet containing all this data?

Let's suppose that your program is already organized to have the header in one struct and the data in another struct . For example, you might have these data structures:

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

Let's call this organization "host format". It really doesn't matter to me what the host format is, as long as it is useful to the rest of your program. Let's call the format that you will pass to the send() call "network format". (I chose these names to match the htons (host-to-network-short) and htonl (host-to-network-long) names.)

Here are some conversion functions that we might find handy. Each of these converts your host format structures to a network format buffer.

#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);
}

To send your data, do this:

...
char buffer[18];
htonHeaderData(myPacketHeader, myPacketData, buffer);
send(sockfd, buffer, 18, 0);
...

Again, you don't have to use the header and data structs that I defined. Just use whatever your program needs. The key is that you have a conversion function that writes all of the data, at well-defined offsets, in a well-defined byte order, to a buffer, and that you pass that buffer to the send() function.

On the other side of the network connection, you will need a program to interpret the data it receives. On that side, you need to write the corresponding functions ( ntohHeader , etc). Those function will memcpy the bits out of a buffer and into a local variable, which it can pass to ntohs or ntohl . I'll leave those functions for you to write.

Well, typically it would look like it's preparing that packet structure into a memory buffer (making judicious calls the the htonl family of functions).

If would then use the send , sendto , sendmsg or write functions, hopefully with a lot of care taken with the length of the buffer and good error handling/reporting.

(Or one of the Win32 apis for the send, if that is the target plateforms.)

You'll find a good presentation about all this at Beej's Guide to Network Programming .

Specifially for the byte packing part (with endian consideration), look at the serialization topic. (There's way more detail in that section than what you need for plain fixed-size integer data types.

The code would look different depending on the OS's networking library (*nix uses Berkeley sockets , Windows uses Winsock , etc.). However, you could create a struct containing all the data you wanted to send in a packet, eg,

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

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

assuming a 32-bit int size.

Edit:

As someone kindly reminded me in the comments, don't forget about converting to and from Network Order . Networking libraries will have functions to assist with this, such as ntohs , nothl , htons , and htonl .

One simple answer is that it would be sent in the format that the receiver expects. That begs the question a bit, though. Assuming the data is a fixed size as shown and the receiving end expects, then you could use a packed (1 byte alignment) structure and store the data in each field. The reason for using 1 byte alignment is that it is typically easier to make sure both ends are expecting the same data. Without 1 byte alignment, then the structure would possibly look different based on compiler options, 32-bit versus 64-bit architecture, etc.) And, typically, it is expected that you would send the values in network byte order if the hex values are integers. You can use functions such as htons and htonl (and possibly htobe64 if available) to convert them.

Assuming that the data is in the structure with the desired byte order, then the send call may be something like this:

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

That assumes that mystruct is declared as an instance of the structure as opposed to a pointer to the structure.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM