如何使用给定的消息规范构造请求消息,然后发送到服务器思想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);
答案 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 字节的值。您可能不需要其中的两个来存储您的单个值。