0

I'm trying to communicate via socket between windows (client) and linux (server). I can start the communication, but when I send data, problems arise. The main problem is that I try to send 2 data double and 1 data ulong but when I read them on the server (Linux) I can not read a correct value. If I send, for example, double1 = 1.5, I receive 0.0000 on the server. Then if I send a double1 = 550.0 I receive on the server -12382718421... (trash)

I have tried to use htonl, ntohl, etc. It does not work I have tried to reorder the bytes of the frame I send from the client, that is, send B0 ... B7 instead of B7 ... B0 ... It does not work. I have been looking for information about it but I can not find anything, except that socket communication between different operating systems is possible, therefore I know there is a solution.

My questions are:

1 - Does htonl and nthol only work with integers? Can I use those conversions for float data?

2 - What is the byte order of a frame in linux and windows?

3 - I know that the sendto () function returns the number of bytes that are being sent. What I send are 20Bytes -> (2 * 8Bytes (double) + 1 * 4Bytes (ulong)) = 20Bytes. But the function returns 24Bytes, how can this be? Is it due to UDP protocol headers or is it additional information that includes Windows?

Thanks all.

PD1: The programming of the sockets is correct.

PD2: Between Linux-Linux I have no problem and I can send data correctly.

Londo
  • 157
  • 5
  • Show your code. And are you accounting for how long is usually a different size on Linux and windows systems? – Shawn Feb 04 '19 at 17:03
  • I put a proposal in my answer to exchange a struct containing an _unsigned long _and 2 doubles, compatible even Windows and Linux do not use the same size for an _unsigned long_ (64b / 32b) and of course even they not have the same endian – bruno Feb 04 '19 at 18:13
  • You could edit the key part of your deleted answer into the question. Most likely either the size of one field wasn't what you expected, or there was some padding or alignment issue. But you can tell this for certain by examining the memory, or printing sizeof/offsetof each field manually. – Useless Feb 04 '19 at 21:43

2 Answers2

2

1 - Does htonl and ntohl only work with integers? Can I use those conversions for float data?

Those functions are only defined for integers. You need to know the format each platform stores doubles in (including endianness) to decide whether you need to re-order the bytes or perform some other conversion.

2 - What is the byte order of a frame in linux and windows?

The question doesn't make sense. You decided what to put in this packet, so the byte order is just whatever you sent.

Traditionally wire formats are often big-endian, but lots of modern formats are little-endian on the grounds that most peers will be using x86.

For your specific case, the fact that you're receiving on Linux is less relevant than the architecture Linux is running on. You haven't mentioned that, but assuming x86_64, the endianness and double format are probably the same as for your sending code.

NB. You should probably use the fixed-size types from <stdint.h>, for example uint64_t instead of unsigned long (assuming that is what you intended). Types like long specifically can be different sizes in different ABIs even on the same platform.

3 - I know that the sendto () function returns the number of bytes that are being sent. What I send are 20Bytes -> (2 * 8Bytes (double) + 1 * 4Bytes (ulong)) = 20Bytes. But the function returns 24Bytes, how can this be? Is it due to UDP protocol headers or is it additional information that includes Windows?

Show your code. The return value from sendto shouldn't be larger than the length argument you passed, so maybe that value isn't what you thought.

The extra length definitely isn't IP or UDP headers, which are longer than 4 bytes anyway.


I have tried to use htonl, ntohl, etc. It does not work

Stop flailing around trying different transforms in the hope one works.

Print a hex dump of your buffer before sending. Print a hex dump of your buffer after receipt. Fill out the structure you expected to see in the receiver and hex dump that, so you can see the difference.

Useless
  • 64,155
  • 6
  • 88
  • 132
0

Does htonl and nthol only work with integers? Can I use those conversions for float data

To manage float you can for instance use an union to transform float in a uint32_t and vice versa, or use a cast of a pointer of float to a pointer of uint32_t and vice versa, of course do not cast a float to uint32_t and reverse

But you seem to work an double rather than float, in that case you need to use htobe64/htole64/be64toh/le64toh (endian.h), if they are not defined under Windows decide the order or the bytes in your packet and define the conversion by yourself

What is the byte order of a frame in linux and windows

The order is only dependent on the CPU, not on the OS

I know that the sendto () function returns the number of bytes that are being sent. What I send are 20Bytes -> (2 * 8Bytes (double) + 1 * 4Bytes (ulong)) = 20Bytes. But the function returns 24Bytes, how can this be? Is it due to UDP protocol headers or is it additional information that includes Windows?

You only access to the payload, all the header/footer/etc are hidden to you

Probably you send a struct rather than making yourself the packet to send, in that case the compilers just do not follow the same padding or the size of a long is 32b on one and 64b on the other ?

Do not send a struct, copy the numbers in a vector of bytes managing the endian, and extract them in the same way


For instance doing all by hand, an unsigned long is read/write on 8 bytes to allow to be compatible even if they have 64b on a host and less on the other :

#include <string.h>

void encDec32(char * a, char * b)
{
  const int e = 1;

  if (*((char *) &e)) {
    memcpy(a, b, 4);
  }
  else {
    a[0] = b[3];
    a[1] = b[2];
    a[2] = b[1];
    a[3] = b[0];
  }
}

void encDec64(char * a, char * b)
{
  const int e = 1;

  if (*((char *) &e)) {
    memcpy(a, b, 8);
  }
  else {
    a[0] = b[7];
    a[1] = b[6];
    a[2] = b[5];
    a[3] = b[4];
    a[4] = b[3];
    a[5] = b[2];
    a[6] = b[1];
    a[7] = b[0];
  }
}

void encodeU32(char ** buffer, uint32_t v)
{
  encDec32(*buffer, (char *) &v);
  *buffer += 4;
}

void encodeU64(char ** buffer, uint64_t v)
{
  encDec64(*buffer, (char *) &v);
  *buffer += 8;
}

void encodeFloat(char ** buffer, float v)
{
  encDec32(*buffer, (char *) &v);
  *buffer += 4;
}

void encodeDouble(char ** buffer, double v)
{
  encDec64(*buffer, (char *) &v);
  *buffer += 8;
}

void encodeUlong(char ** buffer, unsigned long v)
{
  /* force on 8 bytes to be compatible with CPU 32 and 64 */
  encodeU64(buffer, (uint64_t) v);
}

uint32_t decodeU32(char ** buffer)
{
  uint32_t v;

  encDec32((char *) &v, *buffer);
  *buffer += 4;
  return v;
}

uint64_t decodeU64(char ** buffer)
{
  uint64_t v;

  encDec64((char *) &v, *buffer);
  *buffer += 8;
  return v;
}

float decodeFloat(char ** buffer)
{
  float v;

  encDec32((char *) &v, *buffer);
  *buffer += 4;
  return v;
}

float decodeDouble(char ** buffer)
{
  double v;

  encDec64((char *) &v, *buffer);
  *buffer += 8;
  return v;
}

unsigned long decodeUlong(char ** buffer)
{
  /* force on 8 bytes to be compatible with CPU 32 and 64 */
  return (unsigned long) decodeU64(buffer);
}

/* for a struct */

typedef struct S {
  unsigned long u; /* may be on 32 or 64 and not the same size on Linuw and Windows */
  double d1;
  double d2;
} S;

/* b is the block to send, it must be enough long */
/* return the number of bytes to send in a block through UDP */
size_t encodeS(char * b, S * s)
{
  char * b0 = b;

  encodeUlong(&b, s->u);
  encodeDouble(&b, s->d1);
  encodeDouble(&b, s->d2);

  return b - b0;
}

/* b is the block read through UDP */
void decodeS(char * b, S * s)
{
  s->u = decodeUlong(&b);
  s->d1 = decodeDouble(&b);
  s->d2 = decodeDouble(&b);
}
bruno
  • 32,421
  • 7
  • 25
  • 37
  • NB. type-punning via a union is fine in this case since only C is specified, but will instantly become Undefined Behaviour if you change to C++. – Useless Feb 04 '19 at 17:28
  • *a cast of a pointer of `float` to a pointer of `uint32_t` and vice versa* And **that** is [a strict-aliasing](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) violation. – Andrew Henle Feb 04 '19 at 18:31
  • @AndrewHenle a float uses 32b for sure and a double 64b for sure too (IEEE), there is no problem at all – bruno Feb 04 '19 at 18:35
  • @bruno *there is no problem at all* [Oh really?](https://stackoverflow.com/a/42636254/4756299) There are more platforms in the world to write code on than x86. – Andrew Henle Feb 04 '19 at 18:42
  • @AndrewHenle About what problem do you speak ? The proposal I give is compatible with 32b and 64b CPU, including mixed. Currently I am on a raspberrypi using a armv7l (32b) – bruno Feb 04 '19 at 18:48
  • @AndrewHenle I do not speak about the case where there is a pointer to an int and a pointer to a float (32b), but about the case of a pointer to a 32b int and a pointer to a float (32b), the two types have 32b **for sure** whatever the compiler/CPU, so again why do you think there is a problem please ? – bruno Feb 04 '19 at 20:02
  • 1
    Is a 32-bit CPU one that has 32-bit integer registers? 32-bit floating-point registers? A 32-bit bus? Instructions that operate on 32-bit objects? 32-bit addresses? If a system has an 8-bit bus, 24-bit addresses, some 16-bit registers, some 32-bit registers, and some 64-bit registers, what is n such that it is an n-bit CPU? – Eric Postpischil Feb 05 '19 at 00:20
  • to be or not to be ? – bruno Feb 05 '19 at 08:10
  • How can the order be dependent only on the CPU and not the OS when there are CPUs that support both big-endian and little-endian order, with the selection made by the OS? – Eric Postpischil Feb 06 '19 at 03:23
  • At that level the OS is not yet started, it does not decide. We can also wonder if the microcode part of the CPU ? We can continue long time like that, but all of that is just noise, very far of the OP and out of the subject. – bruno Feb 06 '19 at 09:13