简体   繁体   中英

C++ Winsock struct send/recv

I am attempting to send a struct via a winsock UDP socket from client to server, but I am not receiving the same bytes that have been sent, or at least not being to reassemble the struct correctly.

struct Header
{
  uint16_t sender;
  uint16_t number;
  uint16_t packetSize;
  uint16_t type; 
};

Sending struct:

Header header;
header.sender = 1;
header.number = 2;
header.packetSize = 3;
header.type = 4;

char buffer[sizeof(Header)];
memcpy(buffer, &header, sizeof(Header));

 int bytesSent = sendto(sendSocket_, data, sizeof(Header), 0, (const sockaddr*)&sendSocketAddr_, sizeof(sendSocketAddr_));
  if (bytesSent > 0)
  {
    cout << bytesSent << endl;
  }

Receiving struct:

char *data = new char[sizeof(Header)];
  int bytesRecv = recv(listenSocket_, data, sizeof(Header), 0);
  if (bytesRecv > 0)
  {
    cout << bytesRecv << endl;
    Header header;
    memcpy(&header, data, sizeof(data));
    cout << header.sender << ": " << header.number << ": " << header.packetSize << ": " << header.type << endl;
}

The same amount of bytes are received as are sent, however once copied back into a new structure on server side only the first two values are correct, and the rest are not.

In this case the struct contains 1, 2, 52428, 52428 when recieved.

Is there something I have missed during sending/receiving, or is it to do with creating the struct on the other end?

First, sending a raw struct over network is highly non portable :

  • you can have endianness differences between sender and receiver
  • you can hava alignment differences between sender and receiver

Except in special cases where you can be sure that sender and receiver share same architecture, os and compiler, never do that .

The normal way would be to use a portable format, either ASCII (robust but often suboptimal) or binary but you must define a non-ambiguous representation. For example as you use 4 uint16_t :

packing :

char buffer[8];
buffer[0] = header.sender >> 8; /* or (header.sender >> 8) & 0xFF if longer ...*/
buffer[1] = header.sender & 0xFF;
buffer[2] = header.number >> 8;
...

Unpacking :

header.sender = (buffer[0] << 8) | buffer[1];
header.number = (buffer[2] << 8) | buffer[3];
...

But if you had a problem of different architectures, you would not get the first two values. You current problem is much simpler : in receiving code you have :

char *data = new char[sizeof(Header)];
....
    memcpy(&header, data, sizeof(data));

And sizeof(data) is sizeof(char *) , that is 4 bytes in a 32 bits machine whereas sizeof(Header) is 4 * 2 = 8 bytes.

You should have instead :

    memcpy(&header, data, sizeof(Header));

BTW, you could avoid the memcpy part.

Sender part :

int bytesSent = sendto(sendSocket_, &header, sizeof(Header), 0, (const sockaddr*)&sendSocketAddr_, sizeof(sendSocketAddr_));

valid since second argument to sendto is a const void * and conversion to void * is automatic.

Receiver :

int bytesRecv = recv(listenSocket_, &header, sizeof(Header), 0);

since second argument to recv is actually a void * .

You can use: #pragma pack(show) to print the alignment as warning and ensure it is the same.

if there is a difference use #pragma pack(n) like this:

#pragma pack(push)
#pragma pack(1)
struct Header{
  ...
};
#pragma pack(pop)

You don't have to memcpy the struct to buffer. you can use your header object directly.

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