3

I'm using WinSock to send UDP packets to the server, I need to send the data in big endian. I'm not sure how to convert the byte order of my structure before sending.

I have a struct like this:

struct ConnectIn
{
    std::int64_t ConnectionID = 0x41727101980;
    std::int32_t Action = 0;
    std::int32_t TransactionID;

    ConnectIn(std::int32_t transactionID)
    {
        TransactionID = transactionID;
    }
};

And at the moment I'm sending like this:

ConnectIn msg(123);
int len = sizeof(msg);
int bytesSent = sendto(s, (char*)&msg, len, 0, (SOCKADDR*)&dest, sizeof(address));

How can I convert the byte order of msg to big endian before sending?

If you're curious, the data I'm sending is for the Bit Torrent UDP tracker protocol.

Drahcir
  • 11,772
  • 24
  • 86
  • 128
  • 4
    You may look at `hton` functions family. – Jarod42 Oct 21 '14 at 14:03
  • possible duplicate of [Passing a structure through Sockets in C](http://stackoverflow.com/questions/1577161/passing-a-structure-through-sockets-in-c) – edmz Oct 21 '14 at 14:43
  • @black - I don't think it's a duplicate question. The other question is about `C` not `C++`. Also the other question is about serialization or something, it's poorly worded with bad english, it's not easy to understand. – Drahcir Oct 21 '14 at 15:06
  • Sending raw struct over socket (or storing it to file, or anything like that) is usually a bad idea. Sooner or later something changes in the struct, due to multitude of possible reasons, and then your protocol/format breaks. Serialize the struct to a byte array, send that, then on receiving and deserialize from the received byte array. – hyde Oct 21 '14 at 15:10
  • You need to specify your protocol on a *bit* level first, then implement it religiously. You cannot send structs directly as structs don't have universal representation as bit strings. Integers do, so you need to serialize a sequence of integers of various sizes (struct fields) into a bit string, and send that. – n. m. could be an AI Oct 21 '14 at 15:43
  • @n.m. Sorry, I don't understand. The protocol is for UDP torrent trackers, it's already been specified - link in question. – Drahcir Oct 21 '14 at 15:45
  • OK so you need to implement an existing protocol, just skip the "specify your protocol" part. – n. m. could be an AI Oct 21 '14 at 15:51

3 Answers3

4

If you want to do this manually then what you do is swap each member individually. You convert the members from the host computer's byte ordering to the network's byte ordering. On Win32 htonll() is for 64-bit integers and htonl() is for 32-bit integers:

#include <Winsock2.h>

ConnectIn msg(123);

msg.ConnectionID = htonll(msg.ConnectionID);
msg.Action = htonl(msg.Action);
msg.TransactionID= htonl(msg.TransactionID);

Then you might also want to send the members individually, to avoid relying on the host system's struct layout. The Windows ABI doesn't insert any padding in this struct, but perhaps for some other struct you use it does. So here's the basic idea:

char buf[sizeof msg.ConnectionID + sizeof msg.Action + sizeof msg.TransactionID];
char *bufi = buf;

std::memcpy(bufi, &msg.ConnectionID, sizeof msg.ConnectionID);
bufi += sizeof msg.ConnectionID;
std::memcpy(bufi, &msg.Action, sizeof msg.Action);
bufi += sizeof msg.Action;
std::memcpy(bufi, &msg.TransactionID, sizeof msg.TransactionID);
bufi += sizeof msg.TransactionID;

int len = sizeof buf;
int bytesSent = sendto(s, buf, len, 0, (SOCKADDR*)&dest, sizeof(address));

Then on the receiving side you use the appropriate ntoh*() functions for 64-bit and 32-bit types to convert from the network's byte ordering to the receiving host's byte ordering.

bames53
  • 86,085
  • 15
  • 179
  • 244
  • You said "***If you want to do this manually***", does that mean there's an alternative to *manual*? – Drahcir Oct 21 '14 at 14:56
  • @Drahcir Alternatively you could use some serialization or network library that takes care of it for you. – bames53 Oct 21 '14 at 15:07
  • 1
    Note that you are using destination address parameter of `sendto`. That implies datagram protocol like UDP. IOW, the code in this answer sends 3 separate packets instead of one, which probably is not what the asker wants. – hyde Oct 21 '14 at 15:12
  • In the end I decided to write my own template function, so that it can swap the bytes of other types, but thanks for giving me the general idea. – Drahcir Oct 22 '14 at 14:17
  • @Drahcir Yeah, a template function is a good way to implement this without repeating too much. It's usually what I do myself if a project doesn't already have a method. – bames53 Oct 22 '14 at 14:59
3

Yes, the Network Byte Order (NBO) is Big Endian and so you need to find a way to send that structure on the web.

What you're currently doing won't work: you're sending the whole struct but the receiver may have a different endianness, padding and so on.
The easiest options are:

  • Sending each field with a protocol-defined layout
  • Third part libraries which handle serialization: Google Protobuf is one of the most common ones.

For the first option, there're some functions which take care of that in the Winsock2 library. These are:

  • (WSA)ntoht (Network to Host t, where t can be short and unsigned)
  • (WSA)htont (Host to Network t, where t can be short and unsigned)

WSA functions are a little bit different and Windows-only.


The Network Programming Guide
Winsock Reference

edmz
  • 8,220
  • 2
  • 26
  • 45
  • Sending the whole struct does work for this protocol, my struct uses the protocol defined layout (except for endianness), although after reading everyone's comments I can understand why it might be bad practice. – Drahcir Oct 22 '14 at 14:21
  • Google Protobuf looks good, but I don't think I can use it for this though. I think the server would have to modify the protocol as well, to accept this new serialized format, is that correct? – Drahcir Oct 22 '14 at 14:22
  • @Drahcir I think it'd have; I'm not sure though. – edmz Oct 22 '14 at 14:32
-1

One option is converting each of the numbers individually

For GCC:

int32_t __builtin_bswap32 (int32_t x)
int64_t __builtin_bswap64 (int64_t x)

For MSVC:

unsigned short _byteswap_ushort(unsigned short value);
unsigned long _byteswap_ulong(unsigned long value);
unsigned __int64 _byteswap_uint64(unsigned __int64 value);
lisu
  • 2,213
  • 14
  • 22
  • 1
    That's only the way you transform data. That data, though, may already be in NBO. – edmz Oct 21 '14 at 14:23
  • 2
    @lisu: your platform may already be in big endian, so *bswap* will transform your data in little endian. – Jarod42 Oct 21 '14 at 14:37
  • Ok, I see what you mean, but the question is specifically about the conversion to big endian, that's why I didn't mention it. – lisu Oct 21 '14 at 14:51
  • @lisu Indeed. Those functions have no idea of BE/LE. – edmz Oct 21 '14 at 15:08