Can we create a type buffer in a C ++ structure on the client when the server sends data as a c-structure?

I have server, client processes written in C with the names NetworkServer.c and NetworkClient.c, and these 2 interact using Linux sockets. When a client sends a request as shown below to receive ethernet statistics,

// rxbuf - character array of 128K // ETHERNET_DIAGNOSTIC_INFO - structure typedefed recv(sockfd, rxbuf, sizeof(ETHERNET_DIAGNOSTIC_INFO), 0) 

the server fills the data in rxbuf (like ETHERNET_DIAGNOSTIC_INFO, because the server also uses the same copy of the header file where this structure is defined) and sends the data. As soon as the client receives, he will look as follows to receive the data.

 ETHERNET_DIAGNOSTIC_INFO *info = (ETHERNET_DIAGNOSTIC_INFO *) rxbuf; 

The structure is defined in NetworkDiag.h, as shown below.

 #ifdef __cplusplus extern "C" { #endif typedef struct ETHERNET_DIAGNOSTIC_INFO { uint32_t cmdId; unsigned long RxCount[MAX_SAMPLES]; unsigned long TxCount[MAX_SAMPLES]; time_t TimeStamp[MAX_SAMPLES] ; char LanIpAddress[20]; char LanMacAddress[20]; char WanIpAddress[20]; char LanDefaultGateway[20]; char LanSubnetMask[20]; char LanLease[5000]; }ETHERNET_DIAGNOSTIC_INFO; 

This works fine.

Now there is a requirement that I need to create a C ++ file that should work as a client (I deleted the client C file and the server remains as a c file). I defined a header file to define the structure as shown below.

 struct ETHERNET_DIAGNOSTIC_INFO { uint32_t cmdId; unsigned long RxCount[MAX_SAMPLES]; unsigned long TxCount[MAX_SAMPLES]; time_t TimeStamp[MAX_SAMPLES] ; char LanIpAddress[20]; char LanMacAddress[20]; char WanIpAddress[20]; char LanDefaultGateway[20]; char LanSubnetMask[20]; char LanLease[5000]; }; 

I basically removed the C ++ defender and typedef and using the code below in the client.cpp file to get the result from the server.

 if(recv(sockfd, rxbuf, sizeof(ETHERNET_DIAGNOSTIC_INFO), 0) > 0) { ETHERNET_DIAGNOSTIC_INFO *info = reinterpret_cast<ETHERNET_DIAGNOSTIC_INFO *> (rxbuf); } 

I do not get the right results. Values ​​in the structure are inappropriate (some values ​​are true, but many values ​​are inappropriate). I also tried casting of type C, but did not use.

I doubt that we cannot create a type buffer in a C ++ structure on the client when the server sends data as a c-structure. Is it correct? Can someone please let me know how to solve this problem?

+6
source share
4 answers

There are several problems with this approach:

  • Endianness may vary between server and client machine

    Then you need to defuse the numbers and time_t .

  • The packaging structure may differ from the code compiled on the server (C ++) and on the client (C)

    Then you need to use a protocol to send data, for example, binary ASN, protobuf or many others.

  • if(recv(sockfd, rxbuf, sizeof(ETHERNET_DIAGNOSTIC_INFO), 0) > 0) no guarantee recv will accurately read sizeof(ETHERNET_DIAGNOSTIC_INFO) bytes.

    You need to wrap this in a while (the code is a sample and may not be compiled ):

.

 int left = sizeof(ETHERNET_DIAGNOSTIC_INFO); char *ptr = rxbuf; int rd; while(left>0) { rd=recv(sockfd, ptr, left, 0); if(rd==0) { if(left>0) return SOCKET_CLOSED_PREMATURELY; else return SOCKET_DONE; } else if(rd==-1 && errno==EAGAIN) { //do again continue; } else if(rd==-1 && errno!=EAGAIN) { return SOCKET_ERROR; } left = left - rd; ptr=ptr+rd; } 

The right way to send binary data is to use protobuf or apache thrift, or ASN, or come up with something yourself.

+8
source

You may be able to do this, but you will probably encounter serious serious problems when trying:

  • Different compiler and compiler options will group and align structures differently to optimize for a particular processor architecture. There is absolutely no guarantee that members of the structure will line up exactly next to each other if you are not playing with pragmas.
  • Different processors will use different byte orders for things like integers and floating point values. If you are going to exchange data between the client and server (or vice versa), you need to explicitly determine the byte order, and then make both sides meet this definition, regardless of the native order.
  • Values ​​of the unsigned long type will have different sizes based on the processor architecture intended for the compiler. To reliably exchange data, you will need to explicitly determine the size of the transmitted values.

For these reasons, I prefer to write functions (or methods) that will explicitly pack and unpack messages as they are exchanged. By doing so, you will be subject to much smaller, seemingly mysterious errors.

+2
source

A few possible explanations for spring:

  • Different packaging of ETHERNET_DIAGNOSTIC_INFO between C struct and C ++ struct .
  • (less likely) Various rxbuf alignments (you don't see where this pointer rxbuf from). In C or C ++, there is no guarantee that reading an int or long that does not lie on the natural boundary (for example, with 4-byte alignment) gives the correct results.
  • That your C and C ++ compilers compile for different ABIs (e.g. 32-bit and 64-bit, respectively). Please note that sizeof(time_t) == 4 on a 32-bit platform and 8 on many 64-bit platforms.

All these problems point in the same direction: comparing a struct with the layout of wired data, as it is really not portable and problematic.

If you really insist on this, you will need to do the following:

  • Use the #pragma pack (or better: if you use the C ++ 11 __attribute__ ((__packed__))) compiler __attribute__ ((__packed__))) . Even then you can get surprises.
  • Determine which byte orders you intend to use and swap bytes bytes bytes bytes with htons() and friends. The convention is that for multibyte values ​​there must be ellipsis over TCP / IP.
  • Make sure that the buffer you call recv() is aligned - possibly with a 4-byte boundary.

A more robust approach is to read the input buffer as a stream of bytes, restoring any multibyte fields if necessary.

+1
source

Yes, you can, since the buffer is just a representation of the structure byte sent by the other side. After you have processed the byte order, you can simply point the buffer pointer to a pointer to the type of your structure.

In C ++ you can write, for example, ETHERNET_DIAGNOSTIC_INFO* NewPtr = reinterpret_cast<ETHERNET_DIAGNOSTIC_INFO*>(buffer); This will do what you need if you do not run the older C ++ compiler, unable to understand the syntax of C ++ 11. However, depending on your compiler, an error may occur when filling in the data.

If you define bit fields and assemble the structure on both sides, you will still be fine. Ask if you need help, but Google is your friend.

I doubt that we cannot create a type buffer in a C ++ structure on the client when the server sends data as a c-structure. Is it correct?

EDIT: You can transfer any binary data generated by any programming language to the readable part of the code in your program. After all, it's all about bits and bytes. Thus, you can transfer any data from any program to any data in any other program. Could you quickly print sizeof (ETHERNET_DIAGNOSTIC_INFO) on both sides and see if they fit?

-1
source

All Articles