【问题标题】:When sending an array of int over TCP, why are only the first amount correct?通过 TCP 发送 int 数组时,为什么只有第一个数量是正确的?
【发布时间】:2010-10-15 01:01:25
【问题描述】:

在我之前的问题 (Why do I get weird results when reading an array of integers from a TCP socket?) 之后,我想出了以下代码,它似乎可以工作。代码示例适用于少量数组元素,但一旦变大,数据最终会损坏。

这是通过 TCP 发送 int 数组的代码:

#define ARRAY_LEN 262144

long *sourceArrayPointer = getSourceArray();

long sourceArray[ARRAY_LEN];
for (int i = 0; i < ARRAY_LEN; i++)
{
    sourceArray[i] = sourceArrayPointer[i];
}

int result = send(clientSocketFD, sourceArray, sizeof(long) * ARRAY_LEN);

这是接收 int 数组的代码:

#define ARRAY_LEN 262144

long targetArray[ARRAY_LEN];
int result = read(socketFD, targetArray, sizeof(long) * ARRAY_LEN);

前几个数字很好,但在数组的下方,数字开始完全不同。最后,数字应该是这样的:

0
0
0
0
0
0
0
0
0
0

但他们实际上是这样出来的?

4310701
0
-12288
32767
-1
-1
10
0
-12288
32767

这是因为我使用了错误的发送/接收大小吗?

【问题讨论】:

  • 顺便说一句,在发送之前将数组内容复制到本地数组变量中是没有意义的 - 这只是浪费内存和 CPU 周期。
  • 当我尝试使用指针而不是复制到数组时,写入返回-1。这就是我使用复制到数组方法的原因。

标签: c sockets tcp


【解决方案1】:

read(..., len) 的调用不会从套接字读取len 字节,它最多读取len 字节。您的数组相当大,它将被拆分为许多 TCP/IP 数据包,因此您的 read 调用可能只返回数组的一部分,而其余部分仍在“传输中”。 read() 返回它收到的字节数,所以你应该再次调用它,直到你收到你想要的一切。你可以这样做:

long targetArray[ARRAY_LEN];

char *buffer = (char*)targetArray;
size_t remaining = sizeof(long) * ARRAY_LEN;
while (remaining) {
  ssize_t recvd = read(socketFD, buffer, remaining);
  // TODO: check for read errors etc here...
  remaining -= recvd;
  buffer += recvd;
}

【讨论】:

  • 这听起来是最合乎逻辑的答案。我现在将对此进行测试。
  • 嗯,read(...) 的结果似乎与读取的有效 int 元素的实际数量完全不相关。每次我阅读时,它似乎也会发生变化(即有时它的 ~65593 和其他的 ~49923)。无论如何,我会像以前一样分块阅读。
  • 这一行似乎无法编译:buffer += recvd;我收到错误:算术中使用的“void *”类型指针
【解决方案2】:

以下可以吗?

for (int i = 0; sourceArrayPointer < i; i++)

您正在比较苹果和橙子(读取指针和整数)。由于指向longs 数组的指针> 0(大多数情况下),因此不会执行此循环。因此,在接收端,您正在从一个未初始化的数组中读取数据,这会导致这些不正确的数字被传递)。

最好是:

for (int i = 0; i < ARRAY_LEN; i++)

【讨论】:

  • 一定是错字。第一行将如何编译? (或者我的 C 太生锈了)
  • @erikkallen:这就是为什么第一行有一个问题。
  • 在我们习惯的 Intel 架构上,“int”和“long*”(指向 long 的指针)都是 32 位值。 “int”是 32 位,“指向 long”的指针是 32 位宽的内存地址。编译器会让你进行比较(但它可能会引发警告)
  • 另外:假设 sizeof(long*) == sizeof(int) 不可移植!
  • 抱歉,这不是问题 - 我从原始代码中重新输入了它并打错了字。问题仍然存在。我在for循环之后打印了结果,并且要传输的数据似乎是准确的;问题在于使用 read 而不是 for 语句。
【解决方案3】:

【讨论】:

  • 我认为 Dirkgently 的答案是 OP 看到的问题,但您的观点是有效的,需要解决。 +1
【解决方案4】:

与此问题无关,但如果您想在不同平台上使用 TCP,您还需要注意平台的endianness

如果可以的话,使用 curl 或 ACE 之类的网络库要简单得多(此外,您还可以在更高级别学习更多内容,例如设计模式)。

【讨论】:

    【解决方案5】:

    没有什么可以保证 TCP 将如何将您发送到流的数据打包 - 它只保证它会在应用程序级别以正确的顺序结束。所以你需要检查结果的值,并继续阅读,直到你读到正确的字节数。否则,您将不会读取全部数据。使用 long 数组而不是 byte 数组会使这变得更加困难 - 数据可能以任意数量的块发送,这些块可能不会与长边界对齐。

    【讨论】:

      【解决方案6】:

      我发现这里有很多问题。首先,这就是我理解的重写您的发送代码的方式。我假设 getSourceArray 总是返回一个指向大小为 ARRAY_LEN 的静态或分配缓冲区的有效指针。我还假设您在代码后面不需要 sourceArrayPointer。

      #define ARRAY_LEN 262144
      
      long *sourceArrayPointer = getSourceArray();
      
      long sourceArray[ARRAY_LEN];
      long *sourceArrayIdx = sourceArray;
      
      for (; sourceArrayIdx < sourceArray+ARRAY_LEN ; )
          sourceArrayIdx++ = sourceArrayPointer++;
      
      int result = send(clientSocketFD, sourceArray, sizeof(long) * ARRAY_LEN);
      if (result < sizeof(long) * ARRAY_LEN)
          printf("send returned %d\n", result);
      

      查看您的原始代码,我猜您的 for 循环被搞砸了,并且从未执行,导致您发送内存中的任何随机垃圾文件 sourceArray 指向。基本上你的情况

      sourceArrayPointer < i;
      

      几乎可以保证第一次失败。

      【讨论】:

      • 再次,关于您的最后一点,这是一个错字,原始代码中不存在此错误。还是谢谢。
      猜你喜欢
      • 2022-10-29
      • 2019-04-29
      • 2017-10-21
      • 2017-09-11
      • 1970-01-01
      • 2016-09-19
      • 2010-12-16
      • 2015-08-06
      • 1970-01-01
      相关资源
      最近更新 更多