在Big Endian中将int类型的结构放入ostream以转换为char *

时间:2014-11-22 23:03:49

标签: c++ sockets serialization network-programming deserialization

我在UDP套接字上交换了一条消息,我使用structuintN_t的{​​{1}}实施了这些套接字。

传递给UDP套接字的任何数据都必须在stdint.h中,当然,还需要Big Endianchar *类型。

我运行此程序的系统是基于Little Endian的。

我不熟悉序列化,并且基于SO的答案,我尝试const char *以相反的顺序将数据发送到put()来执行此操作。 (详见下面的代码)

ostringstream

但是,当我将struct中的所有数据放入void put_u16_as_big(ostringstream& oss, uint16_t data) { oss.write((const char *)&data, sizeof(data)); if (is_big_endian()) { oss.write((const char *)&data, sizeof(data)); } else { const char * ptr = (const char *)&data; unsigned int s = sizeof(data); for(unsigned int i=0; i < s; ++i) { oss.put(ptr[s-1-i]); } } } 的实例并调用ostringstream方法时,数据的大小不是它应该的大小(小于8字节* 2 + 8字节* 3 *所有服务器的数量,如上表所示),以及该字符串中的str()方法,它进一步缩小(至8)。

最后,当在另一方收到它时,大小为1。

如何将我的结构序列化为data()并发送和接收而不会丢失任何数据?

char *包含structuint16_t类型的字段,以及另一个包含uint32_t,{{1 }和uint16_t

请帮帮我。我几天都在努力解决这个问题,但没有运气......

谢谢。

1 个答案:

答案 0 :(得分:2)

使用套接字API htons()(主机到网络短)和htonl()(主机到网络长)函数将多字节整数从本地机器的本机端转换为网络字节顺序(big端)。在大端系统上,操作是无操作的。在little-endian系统上,字节被交换。您可以使用ntohs()(网络到主机短路)和ntohl()(网络到主机长)将多字节整数从网络字节顺序转换为本地机器的本机端。

请注意,某些功能(如inet_addr()gethostbyname()getaddrinfo()会报告已经按网络字节顺序排列的IPv4地址,因此请勿使用htonl()和{{ 1}}在这些值上,按原样使用它们。

然后,定义一个字节对齐的ntohl()来保存您的邮件数据,然后将其按原样传递给struct不要将其转换为sendto()。不要被ostringstream期望sendto()的事实所困惑,这只是出于历史原因。它实际上需要原始字节,而不是字符串。

试试这个:

char*

#pragma pack(push, 1) // or your compiler's equivalent

struct sMsgHeader
{
    // General Message header fields here...
};

// ...

struct sRouteUpdateServer
{
    uint32_t serverIP;
    uint16_t serverPort;
    uint16_t reserved;
    uint16_t serverID;
    uint16_t cost;
};

struct sRouteUpdateMsg
{
    sMsgHeader header;
    uint16_t numUpdateFields;
    uint16_t serverPort;
    uint32_t serverIP;
    sRouteUpdateServer servers[some max value here];
};

#pragma pack(pop) // or your compiler's equivalent

或者,如果您的编译器支持sRouteUpdateMsg msg; // fill msg.header as needed... msg.numUpdateFields = htons(...); msg.serverPort = htons(...); msg.serverIP = ...; for (int i = 0; i < numberOfServers; ++i) { msg.servers[i].serverIP = ...; msg.servers[i].serverPort = htons(...); msg.servers[i].reserved = 0; msg.servers[i].serverID = htons(...); msg.servers[i].cost = htons(...); } //... sendto(..., (char*)&msg, sizeof(sMsgHeader)+8+(sizeof(sRouteUpdateServer)*numberOfServers), ...); (或您自己编写):

offsetof()

在接收方做相反的事情。使用sendto(..., (char*)&msg, offsetof(sRouteUpdateMsg, servers)+(sizeof(sRouteUpdateServer)*numberOfServers), ...); 接收消息数据,然后根据需要调用recvfrom()转换消息的多字节整数,然后再处理消息。