【问题标题】:Send C struct over TCP socket通过 TCP 套接字发送 C 结构
【发布时间】:2016-01-11 21:48:44
【问题描述】:

看了很多与我的问题相关的答案,但在我的条件下确实用不上。

我正在 Linux 域下使用 C 语言的套接字编程构建一个网络模块。 我必须实现一个函数,它可以发送一个由 int、char、char * 和一些其他结构(嵌套结构)组成的结构。

struct EnQuery
{
   char  type[6]; // insert, select , update , delete
   char * columns; //note here, it's an array of big, {name, age, sex, position, email}  not in string
   struct Values * values; //an array of values(another struct), {tom, 23, male, student, tom@compan.com}  is represented by the second struct values, not in string
   struct condition * enCondition; //  array of condition,  "name=tom and age>30" is represnted by the third struct condition
   short len;
   short rn; 
};

struct Values
{
    char * doc;
    char  key[2];
    char  s;
};

struct condition
{
     short k;              
     struct condition * children;
};

上面是我要发送的结构。 我正在声明变量并使用 send() 函数通过套接字发送。

我必须如何通过套接字发送 char *? 或者有没有办法方便地调整 char 数组的长度?

P.S 我不能使用外部库

【问题讨论】:

  • 只需确定您要使用的有线格式并编写代码来转换为该有线格式。查看现有规范(BER、XML、JSON 等)以获取想法。
  • 我不能使用任何外部库..
  • 你的 strcts 有指针,不能传输,因为它们在接收端无效——你需要序列化和反序列化数据——你怎么做会有一百万个很好的答案。
  • 所有带有星号 (*) 的东西都需要序列化,而不仅仅是chars。如果您没有任何指针,您可以按原样发送结构(假设接收器具有相同的字节序和内存填充等)。
  • 不要使用结构体作为网络协议。使用网络协议作为网络协议。首先在八位字节中定义协议,然后自己编写一个 API 来发送和接收它。

标签: c sockets pointers struct network-programming


【解决方案1】:

如何通过套接字发送 char *?

显然,发送指针值并不是一件有用的事情,因为指针不会指向接收计算机上的任何有效内容。

因此,您必须发送指针指向的数据,而不是发送指针。一个典型的方法是首先发送指针指向的字节数:

uint32_t sLen = strlen(doc)+1;  // +1 because I want to send the NUL byte also
uint32_t bigEndianSLen = htonl(sLen);
if (send(sock, &bigEndianSLen, sizeof(bigEndianSLen), 0) != sizeof(bigEndianSLen)) perror("send(1)");

....然后发送字符串的字节:

if (send(sock, doc, sLen, 0) != sLen) perror("send(2)");

在接收端,你会做相反的事情:首先接收字符串的长度:

uint32_t bigEndianSLen;
if (recv(sock, &bigEndianSLen, sizeof(bigEndianSLen), 0) != sizeof(bigEndianSLen)) perror("recv(1)");
uint32_t sLen = ntohl(bigEndianSLen);

...然后接收字符串的数据:

char * doc = malloc(sLen+1);
if (recv(sock, doc, sLen, 0) != sLen) perror("recv(2)");
doc[sLen] = '\0';  // paranoia:  make sure the string is terminated no matter what

请注意,此示例代码有点幼稚,因为它无法正确处理 send() 或 recv() 返回的值不是传递给它们的字节数的情况。生产质量代码将正确处理错误(例如通过关闭连接),并且还可以正确处理 send() 或 recv() 发送/接收仅传输一些请求的字节的情况(通过调用 send()/ recv() 稍后再次为剩余的未发送/未接收字节)。

【讨论】:

  • 这也适用于 char 吗?我该如何施放这个?
  • char 不是指针,所以你可以直接发送一个 char,类似于发送一个 int (实际上比 int 更容易,因为一个 char 只有 1 个字节你不需要担心关于字符的字节序转换问题)。例如。字符 s = 'J';发送(袜子, &s, 1, 0);
  • 你能给我一个类似上面的例子吗?提前谢谢你
  • 我正在尝试实现这一点,但由于您只指定了一个只有一个元素的示例,我将如何添加另一个元素?比方说,发送 key[2] 后跟 doc?一个具体的例子将不胜感激,并将提供很多帮助。非常感谢
  • 我目前无法发送两个元素。如果您能帮我解决这个问题,我将不胜感激
【解决方案2】:

您需要做出选择:要么使用“打包”函数在发送端序列化内存中的数据结构,通过线路发送并使用解包函数反序列化(直截了当),或者直接动态序列化/反序列化到套接字(作为第一个版本的优化,以后做的更好)。

您可以从这个开源代码中获得一些灵感,例如: https://code.google.com/p/protobuf-c/

【讨论】:

  • 你能用指定的结构做一些例子吗?提前致谢
  • 如何尝试编写一个非常简单的包版本,仅将固定长度的项目复制到 char[] 中,然后用代码扩展它以复制条件数组。一旦你能做到这一点,你就可以使用类似的代码将 char* 添加到 char[] 的末尾,所以现在你可以处理一个 Values 结构。然后,扩展该代码以处理一组值,最后您拥有执行 EnQuery 结构的所有工具。从小事做起,边做边调试,你可以弄清楚如何完成大任务。
  • 直接序列化到套接字几乎不是一种优化——它会使性能变得更糟。
  • 不幸的是,这与我约会。我最后一次直接写到套接字是在 10 Base-T 刚上线并且还没有普及的时候。
【解决方案3】:

用“类型编号”定义一个枚举。使用您的示例:

查尔 字符串 短 着色 查询 T值 条件 流行音乐

定义一些与类型进行或运算的标志位:

Tarray -- 基类型数组
现在输出类型号。以实际数据为准。例如,输出一个 Values 结构:
Tvalues
Tstring,doc 的数据(inc EOS)
查尔 | Tarray,2,关键数据
Tchar,s 值
Tpop
任何聚合(例如结构)都会执行与 Tpop 配对的隐含“Tpush”

现在您有了一个字节流,您可以使用某种状态机递归地处理它。对于上述所有类型,您需要递归函数来导出/导入。在导入时,关键是类型号是您发送的内容

【讨论】:

    【解决方案4】:

    结构是(ANSI) C language 可用的数据类型。我相信您正在寻找以下函数来在您的结构中执行数据发送。

    1. socket()
    2. read()
    3. write()

    请记住,这些是IPC(也称为signals)、TCP/IP 和/或UDP 通信的最低要求。

    对于客户端/服务器架构的简单实现(尽管这些天已经彻底过时)参考; client/server。您可能会更好地将研究重点放在peer to peer 通信上,因为每个对等点都持有彼此的连接信息,从而为您提供一组更强大的故障转移;看这里:

    【讨论】:

      猜你喜欢
      • 2017-04-24
      • 2018-06-26
      • 1970-01-01
      • 2014-09-02
      • 2016-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-22
      相关资源
      最近更新 更多