C套接字用二进制协议发送多个字段消息

时间:2013-01-24 09:40:56

标签: c

如何使用给定的消息规范构造请求消息,然后发送到服务器思想c套接字?二进制协议用于客户端和服务器通信。以下方法是否正确?

给出消息说明:

Field         Fomat   Length    values
------------  ------  ------   --------
requesID      Uint16    2       20
requestNum    Uint16    2       100
requestTitle  String    10      data sring


    /************** approach 1 ****************/
    typedef unsigned short uint16;
    typedef struct {
        uint16 requesID [2];
        uint16 requestNum [2];
        unsigned char requestTitle [10];
    }requesMsg;
    …
    requesMsg rqMsg;

    memcpy(rqMsg.requesID, "\x0\x14", 2);   //20
    memcpy(rqMsg.requesNum, "\x0\x64", 2);  //100
    memcpy(rqMsg.requesTitle, "title01   ", 10);
    …
    send(sockfd, &rqMsg, sizeof(rqMsg), 0);

    /************** approach 2 ****************/
    unsigned char rqMsg[14];
    memset(rqMsg, 0, 14);
    memcpy(rqMsg, "\x0\x14", 2);
    memcpy(rqMsg+2, "\x0\x64", 2);
    memcpy(rqMsg+4, "title01   ", 10);
    …
    send(sock, &rqMsg, sizeof(rqMsg), 0);
    

2 个答案:

答案 0 :(得分:3)

我担心你误解了一些东西:长度列似乎告诉你长度的字节数,所以如果你收到一个uint16,你会收到2个字节。

您的第一种方法可能会导致data structure alignment出现严重问题。如果我在你的鞋子里,我更喜欢第二种方法,并将我自己的字节填入字节数组。

关于填充字段的一般注意事项:对于像uint16等“本地”字段使用memcpy是没用的。它可能有效但只是浪费运行时。您可以填写结构的字段,只需为其分配rqMsg.requesID = 20;

之类的值

另一个问题是二进制协议的字节顺序或endianness的问题。

作为一个整体包,我将实现一个“serializeRequest”函数,它接受结构的字段并根据协议将其转换为字节数组。

答案 1 :(得分:0)

它们至少部分正确,但我更喜欢第一个,因为与手动索引相比,它允许快速和自然的数据操作和访问,并留下更少的错误空间。作为奖励,您甚至可以作为一个整体复制和分配结构值,并且在 C 中它按预期工作。

但是对于任何传出数据,您应该确保使用“打包”结构。它不仅会减少传输到基于数组的实现图的数据量,而且还会确保所有涉及的程序中的字段对齐方式相同。对于我尝试过的大多数 C 编译器(包括 GCC),它可以使用 __attribute__((__packed__)) 属性来完成,但是有不同的编译器需要不同的属性甚至不同的关键字。

如果您的应用程序要在不同的体系结构上运行(ARM 客户端与 x86_64 服务器是一个主要示例),则可能还需要字节序控制。在进行任何计算或数据输出之前,我只是使用一些像这样的简单宏来单独预处理每个字段:

#define BYTE_SWAP16(num)    ( ((num & 0xFF) << 8) | ((num >> 8) & 0xFF) )
#define BYTE_SWAP32(num)    ( ((num>>24)&0xff) | ((num<<8)&0xff0000) | ((num>>8)&0xff00) | ((num<<24)&0xff000000) )

但是您可以使用不同的方法,例如 BCD 编码、单独的解码功能或其他方法。

还要注意 uint16_t 已经是一个 2 字节的值。您可能不需要其中的两个来存储您的单个值。