【问题标题】:c++ posix sockets recv functionalityc++ posix sockets recv 功能
【发布时间】:2012-12-26 12:44:14
【问题描述】:

我有一个可能很无聊的问题要问,我环顾四周,但没有看到直接的答案,我想我可能会在这里得到一个快速的答案。在使用 bsd 套接字的简单 TCP/IP 客户端-服务器选择循环中,如果客户端发送两条同时到达服务器的消息,在服务器上调用 recv 是否会返回捆绑在缓冲区中的两条消息,还是 recv 强制每个要单独阅读不同的到达消息?

我之所以这么问,是因为我在一个无法判断客户端如何构建要发送的消息的环境中工作。通常,recv 报告读取了 12 个字节,然后是 915,然后是 12 个字节,然后是 915,以此类推,以这种交替的 12 到 915 模式......但有时它会报告 927(即 915+12)。我在想,要么是客户端在将它的一些信息发送到服务器之前将其捆绑在一起,要么是消息在调用 recv 之前到达,然后 recv 同时提取所有待处理的字节。所以我想确保我正确理解了 recv 的行为。我想也许我在我的理解中遗漏了一些东西,我希望有人能指出它,谢谢!

【问题讨论】:

    标签: sockets recv


    【解决方案1】:

    TCP/IP 是基于流的传输,而不是基于数据报的传输。在流中,send()recv() 之间没有一对一的关联。这仅适用于数据报。因此,您必须准备好应对多种可能性:

    1. send() 的一次调用可能适合单个 TCP 数据包,并通过对 recv() 的一次调用完整读取。

    2. send() 的一次调用可能跨越多个 TCP 数据包,并且需要对 recv() 进行多次调用才能读取所有内容。

    3. send() 的多次调用可能适合单个 TCP 数据包,并通过对 recv() 的一次调用完整读取。

    4. send() 的多次调用可能跨越多个TCP 数据包,并且需要对每个数据包多次调用recv()

    为了说明这一点,请考虑发送两条消息 - send("hello", 5)send("world", 5)。以下是调用recv()时的几种可能组合:

    "hello" "world"
    "hel" "lo" "world"
    "helloworld"
    "hel" "lo" "worl" "d"
    "he" "llow" "or" "ld"
    

    明白了吗?这就是 TCP/IP 的工作原理。每个 TCP/IP 实现都必须考虑到这种碎片。

    为了正确接收数据,逻辑消息之间必须有明确的分隔,而不是单独调用 send(),因为可能需要多次调用 send() 才能发送一条消息,而多个 recv()呼叫以完整接收一条消息。因此,考虑到前面的示例,让我们在消息之间添加一个分隔符:

    send("hello\n", 6);
    
    send("world", 5);
    send("\n", 1);
    

    在接收端,您可以多次调用recv(),直到收到\n 字符,然后您将处理您在该字符之前收到的所有内容。如果完成后还有剩余的读取数据,请将其保存以供以后处理并再次开始调用recv(),直到下一个\n字符,依此类推。

    有时,不可能在消息之间放置唯一字符(也许消息正文允许使用所有字符,因此没有可用作分隔符的不同字符)。在这种情况下,您需要在消息前面加上消息的长度,或者作为前面的整数、结构化标头等。然后您只需根据需要多次调用recv(),直到您收到完整的整数/标头,然后您根据需要多次调用recv(),以读取与长度/标题指定的字节数一样多的字节。完成后,如果需要,保存所有剩余数据,然后重新开始调用 recv() 以读取下一个消息长度/标题,等等。

    【讨论】:

    • 感谢您非常详细的回复,非常感谢。了解 TCP/IP 实现的具体细节会有很大帮助,希望能帮助其他不了解的人。
    【解决方案2】:

    您唯一可以依靠的是字节的顺序。您不能指望它们如何划分为 recv 调用。有时事情会在端点或沿途合并。事情也可能在此过程中被打破,因此独立到达。听起来您的发件人正在交替发送 12 和 915,但您不能指望它。

    【讨论】:

    • 感谢您提供易于理解的答案和建议,我将重新编写代码以解决此问题。
    【解决方案3】:

    在单个recv 调用中返回两条消息绝对有效(请参阅Nagle's Algorithm)。 TCP/IP 保证顺序(消息中的字节不会混合)。除了在单个调用中一起返回它们之外,单个消息还可能需要对 recv 进行多次调用(尽管对于像描述的那样小的数据包不太可能)。

    【讨论】:

    • 感谢您的直截了当的回复,非常感谢。我想我将不得不重新编写我的代码来解决这个问题! :P
    • 我会说“TCP 保证订单”。 IP 对此无能为力。
    猜你喜欢
    • 1970-01-01
    • 2011-05-20
    • 2021-05-23
    • 1970-01-01
    • 2021-10-29
    • 1970-01-01
    • 2015-09-13
    • 1970-01-01
    • 2021-03-23
    相关资源
    最近更新 更多