如何通过网络套接字可移植地发送 C 结构?

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

假设我有一个 C 结构定义如下:

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

其中 'MAX_WORD' 可以是任何值。 现在,如果我有这个结构的一个实例:

servSendData  myData ;

如果我填充这个实例,然后通过网络发送它,考虑到我希望我的服务器和客户端都在 64 位系统上 运行,这里是否会有任何可移植性问题系统或 32 位系统。

我将按如下方式发送和接收数据:

//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) ;

是的,肯定会存在可移植性问题。

即使在同一平台上的不同编译器中,结构成员的对齐方式也可能不同,更不用说不同平台了。这一切都假设 sizeof(int) 在所有这些人中都是相同的(虽然理所当然,它 通常 是 --- 但你真的想依赖 "usually"并希望最好的?)。

即使两台计算机上的 MAX_WORD 相同(我假设它们从现在开始都是一样的;如果不是,那么你就有麻烦了)。

您需要做的是分别发送(和接收)每个字段。 sizeof(int) 和字节顺序也有问题,所以我添加了对 htonl() 的调用以将系统字节顺序转换为网络字节顺序(逆函数是 ntohl())。它们都 return uint32_t 具有固定的已知大小。

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

你必须注意字节序。

你应该使用 hton() 或 ntoh() 函数,在小端和大端之间进行转换。

您可以使用 structure packing。对于大多数 C 编译器,您可以强制执行特定的结构对齐。它有时用于您需要的用途 - 通过网络传输 struct

请注意,这仍然存在字节序问题,因此这不是一个通用的解决方案。

正如其他依赖者所说,在具有不同 compilers/word size/and endian 结构的不同机器之间复制 C 结构存在实际问题。解决此问题的一种常见方法是将数据转换为独立于机器的格式,通过网络传输,然后将其转换回接收器上的结构。这是一个如此普遍的要求,以至于已经存在多种技术来做到这一点 - spring 在我看来最初是 gsoap and rpcgen 的两个,尽管可能还有许多其他选择。

我主要使用 gsoap,在你通过最初的学习曲线之后,你可以开发强大的解决方案,这些解决方案可以很好地扩展(使用多线程)并为你处理网络和数据转换。

如果您不想走这条路,那么最安全的方法是编写将您的数据to/from转换为标准字符串格式的例程(如果您如果 Unicode 有问题,您还需要考虑到这一点),然后通过网络发送它。

如果您不编写嵌入式软件,在应用程序之间发送数据而不正确序列化很少是个好主意。

同样使用raw sockets,不太方便,感觉有点像"reinventing the wheel"。

许多图书馆都可以帮助您!当然,您不一定非要使用它们,但阅读它们的文档并了解它们的工作原理将有助于您做出更好的选择。您尚未计划的事情可以立即出现(例如,当您想要更新系统时会发生什么,并且消息格式发生变化?)

对于序列化,请阅读这些通用格式:

  • 人类可读:JSON、XML、YAML、其他...
  • 二进制:Protobuf, TPL, Avro、BSON、MessagePack 和许多其他

套接字抽象,查找

  • 提升 ASIO
  • ZeroMQ
  • nanomsg
  • 许多其他人