简体   繁体   中英

How can I portably send a C struct through a network socket?

Suppose I have a C struct defined as follows :

typedef struct servData {
    char max_word[MAX_WORD];
    char min_word[MAX_WORD];
    int word_count ;
} servSendData ;

where 'MAX_WORD' could be any value. Now if I have an instance of this structure :

servSendData  myData ;

And if I populate this instance and then send it over the network, will there be any portability issues here considering that I want my server as well as the client to be running on either a 64-bit system or a 32-bit system.

I am going to send and receive data as follows :

//server side
strcpy(myData.max_word, "some large word") ;
strcpy(myData.min_word, "small") ;
myData.word_count=100 ;
send(sockFd, (char*)&myData, sizeof(myData);

//client side
recv(sockFd, (char*)&myData, sizeof(myData);
printf("large word is %s\n", myData.max_word) ;
printf("small word is %s\n", myData.min_word) ;
printf("total words is %d\n", myData.word_count) ;

Yes, there definitely will be portability issues.

Alignment of structure members can be different even among different compilers on the same platform , let alone different platforms. And that's all assuming that sizeof(int) is the same across all of them (though granted, it usually is --- but do you really want to rely on "usually" and hope for the best?).

This holds even if MAX_WORD is the same on both computers (I'll assume they are from here on out; if they're not, then you're in trouble here).

What you need to do is send (and receive) each field separately. There is also a problem with sizeof(int) and endianness, so I've added a call to htonl() to convert from system to network byte order (the inverse function is ntohl() ). They both return uint32_t which has a fixed, known, size.

send(sockFd, myData.max_word, sizeof(myData.max_word)); // or just MAX_WORD
send(sockFd, myData.min_word, sizeof(myData.min_word));
uint32_t count = htonl(myData.word_count); // convert to network byte order
send(sockFd, &count, sizeof(count));

// error handling!
if((ret = recv(sockFd, myData.max_word, sizeof(myData.max_word))) != sizeof(myData.max_word))
{
    // handle error or read more data
}
... // and so on
// remember to convert back from network byte order on recv!
// also keep in mind the third field is now `uint32_t`, and not `int` in the stream

You have to take care about the endians.

May you should use hton() or ntoh() functions, to convert between little and big endian.

As other relies have stated there are real problems in copying a C structure between different machines with different compilers/word size/and endian structure. One common way to resolve this issue is to transform your data into a machine independent format, transfer it across the network and then transform it back into a structure on the receiver. This is such a common requirement that multiple technologies already exist to do this - the two that spring to my mind initially are gsoap and rpcgen although there are probably many other options.

I've mostly used gsoap and after you get past the initial learning curve you can develop robust solutions that scale well (with multiple threads) and which handles both the networking and data translations for you.

If you don't want to go down this route then the safest approach is to write routines that convert your data to/from a standard string format (if you have issues with Unicode you'll need to take that into account as well) and then send that across the network.

You can use structure packing . With most C compilers, you can enforce a specific structure alignment. It is sometimes used for what you need it - to transfer a struct over a network.

Note that this still leaves endianness issues, so this is not a universal solution.

If you are not writing embedded software, sending data between applications without serializing properly is rarely a good idea.

The same goes for using raw sockets, which is not very convenient, and feels a bit like "reinventing the wheel".

Many libraries can help you with both! Of course, you don't have to use them, but reading their documentation, and understanding how they work will help you make better choices. Things you have not yet planned can come out of the box (like, what happens when you want to update your system, and the message format changes?)

For serialization, have a read on those general purpose formats:

  • Human readable: JSON, XML, YAML, others...
  • Binary: Protobuf , TPL , Avro , BSON, MessagePack, and many others

For socket abstraction, look up

  • Boost ASIO
  • ZeroMQ
  • nanomsg
  • Many others

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