【问题标题】:C++ Socket recv not receiving correct number of bytes?C++ Socket recv 没有接收到正确的字节数?
【发布时间】:2015-02-16 14:07:57
【问题描述】:

我对 C++ 很陌生,但我正在尝试学习一些 TCP 套接字编码的基础知识。无论如何,我已经能够发送和接收消息,但是我想在我的数据包前面加上数据包的长度(就像我在过去制作的 C# 应用程序中所做的那样)所以当我的窗口收到 FD_READ 命令时,我有以下代码仅读取数据包的前两个字节以用作短整数。

char lengthBuffer[2];

int rec = recv(sck, lengthBuffer, sizeof(lengthBuffer), 0);

short unsigned int toRec = lengthBuffer[1] << 8 | lengthBuffer[0];

让我感到困惑的是,在一个数据包进入“rec”变量之后,它表示读取了多少字节是一,而不是二,如果我将 lengthBuffer 设为三个字符而不是两个,它读取三个字节,但是如果是四,它也读三(只有奇数)。我不知道我是否在这里犯了一些非常愚蠢的错误,或者从根本上误解了语言或 API 的某些部分。我知道 recv 不保证会读取任意数量的字节,但如果它只是两个,它不应该多次读取。

【问题讨论】:

  • 您是要查找正在发送和接收的字节数,还是要查找要发送的可用字节数?也许帮助或不基于问题 - stackoverflow.com/questions/12984816/…
  • 可能只是重复,这取决于有多少其他类似的问题。
  • 阻塞模式下 TCP 套接字的“正确字节数”是一个或多个。请参阅 man 页面。你的期望是没有根据的。

标签: c++ sockets


【解决方案1】:

因为您无法假设有多少数据可用,所以您需要不断地从套接字读取数据,直到获得所需的数据量。像这样的东西应该可以工作:

ssize_t rec = 0;
do {
    int result = recv(sck, &lengthBuffer[rec], sizeof(lengthBuffer) - rec, 0);
    if (result == -1) {
        // Handle error ...
        break;
    }
    else if (result == 0) {
        // Handle disconnect ...
        break;
    }
    else {
        rec += result;
    }
}
while (rec < sizeof(lengthBuffer));

【讨论】:

  • 您应该在循环内检查来自recv 的错误,而不是仅仅将返回值添加到rec
  • @Jonathan Potter 感谢您的理解,并感谢 Remy 的编辑。
【解决方案2】:

流式套接字:

套接字通常以流式方式使用:您将收到所有发送的数据,但不一定一次全部收到。您也可能会收到一些数据。

因此,您发送长度的方法是有效的:一旦您收到长度,您就可以加载一个缓冲区,如果需要跨连续读取,直到您得到您期望的所有内容。因此,您必须循环接收,并定义如何处理收到的额外字节的策略。

数据报(面向数据包)套接字:

如果您的应用程序真的是面向数据包的,您可以考虑通过请求linuxwindows socket()、SOCK_DGRAM 或更好的 SOCK_SEQPACKET 套接字类型来创建数据报套接字。

二进制大小数据的风险:

请注意,您发送和接收尺寸数据的方式似乎是不对称的。因此,如果具有不使用相同endian-ness 的 CPU/架构的机器之间的发送和接收,您将面临重大风险。你可以找到here 一些关于如何使你的代码平台/字节序无关的提示。

【讨论】:

  • 您不能只使用 ntohs 和 htons 而不是您提供的链接上给出的建议代码吗?
【解决方案3】:

TCP 套接字是基于流的,而不是数据包(我假设您使用 TCP,因为在数据中发送数据包的长度在 UDP 中没有任何意义)。您一次收到的字节数不必发送太多。例如,您可能发送 10 个字节,但接收者可能会收到 1 + 2 + 1 + 7 或任何组合。你的代码必须处理这个问题,能够接收部分数据并在你获得足够的数据时做出反应(这就是你发送数据包长度的原因)。

【讨论】:

  • length in data does not make any sense in UDP 什么?
  • 对,我明白了,这就是我使用这种方法的原因。抱歉,如果我不清楚(我使用数据包来指代完整的数据段,即使这不是 TCP 的真正正确术语)。我感到困惑的是为什么 recv 只会读取奇数字节而不是缓冲区的整个大小。我发送的数据长度只有 15 个字节(前两个是长度),我的缓冲区是两个字节,所以它不应该需要两次调用来填充该缓冲区。如果我使缓冲区更大并从第一次接收中读取前两个字节,我可以完成这项工作,但我不明白为什么。
  • @AtlasC1 我的意思是在 UDP 数据包中发送数据包的长度没有任何意义
  • @user1869878 无论您使用何种缓冲区大小,您都无法假设获得数据时将如何拆分数据。你的代码必须处理这个问题。
猜你喜欢
  • 2021-12-28
  • 2015-08-19
  • 1970-01-01
  • 2016-06-02
  • 1970-01-01
  • 1970-01-01
  • 2021-08-15
  • 1970-01-01
  • 2012-07-05
相关资源
最近更新 更多