【问题标题】:If you send data over a socket in one call to send(), will it be received in one call to receive()?如果您在一次调用 send() 时通过套接字发送数据,是否会在一次调用 receive() 时接收到数据?
【发布时间】:2011-04-19 12:20:15
【问题描述】:

我已经看到了套接字的几种用法,程序员通过 TCP/IP 套接字发送命令或一些信息,并期望在接收端的一次调用中接收到它。

例如,传输

mySocket.Send("SomeSpecificCommand")

他们假设接收方将在一次调用中接收所有数据。例如:

Dim data(255) As Byte   
Dim nReceived As Long = s.Receive(data, 0, data.Count, SocketFlags.None)
Dim str As String = Encoding.ASCII.GetString(data, 0, n)
If str = "SomeSpecificCommand" Then
    DoStuff()
    ...

上面的例子没有使用任何终止符,所以程序员依赖于套接字实现是不允许的,例如,在第一次调用 Receive() 时返回“SomeSpecif”,以及“cCommand”在稍后对 Receive() 的调用中。 (注意 - 在示例中,缓冲区的大小大于预期的字符串)。

我以前从来没有考虑过这么多,只是假设这种编码是不安全的,并且一直使用分隔符。我是否一直在浪费时间(和处理器周期)?

【问题讨论】:

  • 感谢大家的回答。有些人可能会感兴趣,我开始对此感到疑惑的原因是,在 USB 设备驱动程序中,这个假设是正确的,应该使用它,因为它更有效。

标签: sockets


【解决方案1】:

无法保证它们会同时到达。代码(应用程序的协议)需要处理来自一次发送的数据可能分多块到达的可能性,或者来自多个发送的数据可能一次接收到达的可能性。

【讨论】:

  • 谢谢 - 很高兴知道我没有浪费时间。让我感到奇怪的是,在 USB 设备驱动程序中,这个假设是正确的,应该使用它,因为它的效率要高得多。
  • 如果有参考就好了。您知道套接字文档中的任何地方都说明了这一点吗?
  • 即使看起来效率更高,某些环境也需要多重响应。想一想您请求登录的过程,服务器响应说您已经登录,然后它响应说用户有新消息。对于服务器来说,登录和新消息最好是它自己的过程,并有自己的响应。那个和大多数服务器套接字应用程序都是惰性的,并且在 que 中工作并一次发送一部分请求,而不是准备完整的请求并将其发送出去。
【解决方案2】:

在对 send() 的一次短调用中发送的短 sn-ps 数据通常会在一次对 recv() 的调用中到达,这就是为什么这样的代码大部分时间都可以工作的原因。但是,它并不能保证,因此依赖它是不好的做法。

TCP 会缓冲数据,并可以根据需要将其拆分。 TCP 尝试发送尽可能少的数据包以节省带宽,因此它不会无缘无故地拆分数据。但是,如果它一直在排队一些数据并且来自一次调用 send() 的数据恰好跨越了数据包边界,则该数据将被拆分。

另外,TCP 可以尝试在一个数据包中发送它,但是在到达目的地的路径上的任何地方的路由器都可能会返回并说“这个数据包太大了!”。然后 TCP 会将其拆分成更小的数据包。

【讨论】:

    【解决方案3】:

    通过网络发送数据时,您应该期望您的数据会被分成多个数据包,并构建您的代码和数据来处理这个问题。在您发送少量字节的示例情况下,一切都会正常工作..直到您开始发送更大的数据包。

    如果您希望一次接收一条消息,那么您可以在第一个字节到达后的一段时间内循环读取字节。这很简单,但效率很低。

    可以按照建议使用定界符,但您必须防止意外地将定界符包含在常规数据中。如果您只发送文本,那么您可以使用 null 或一些不可打印的字符。如果您要发送二进制数据,那么这将变得更加困难,因为数据中出现的任何分隔符都需要由发送方转义,而由接收方不转义。

    分隔符的替代方法是在包含消息长度的数据前面添加一个字段。这比使用分隔符更好,因为它消除了转义数据的需要,并且比简单地循环直到计时器到期更好,因为它会更具响应性。

    【讨论】:

    • 我当前的解决方案听起来非常接近这个 - 我响应接收事件并将接收到的数据附加到链接列表。然后我的程序从列表中取出 CRLF 分隔的纯文本命令。该协议允许这些纯文本命令后跟二进制数据,只要在纯文本命令中指定字节数即可。我担心这完全是OTT,但听起来不错。我想知道在实施这种方案上花费了多少全球工时......!
    【解决方案4】:

    不,假设服务器(假设您的客户端)只会向您发送一个套接字响应,这不是一个好主意。服务器可以通过返回多个结果的过程列表运行。我会继续从套接字读取,直到没有任何东西可以拾取,然后等待几毫秒并再次测试。如果什么都没有出现,那么服务器很可能已经完成了响应的发送。

    【讨论】:

    • 在我使用 telnet 服务器之前,我也有同样的假设。我不得不重写大部分 telnet 类。
    • 如果对方背靠背发送两条单独的消息怎么办?我已经看到所有 message-1 和 message-2 的一部分在第一次接收时到达,而 message-2 的其余部分在下一次接收时到达。
    • 我总是一次收到一条单独的消息。确保您只读取每个响应可用的字节,否则您可能正在读取套接字“队列”结构中的下一项。
    • 您还可能有一个 End Of Stream 或类似标记,以帮助分隔响应。
    • 您永远不应该依赖 TCP 保留消息边界。有很多事情可能会出错,例如:您的端点之间的 esome 代理可能会重新排列消息,如果您发送的消息大于 MSS,您的 TCP/IP 堆栈将部分发送它,...
    【解决方案5】:

    有几种类型的套接字。 TCP 使用 SOCK_STREAM,它不保留消息边界。 SOCK_SEQPACKET 套接字确实保留了消息边界。

    编辑:SCTP 同时支持 SOCK_STREAM 和 SOCK_SEQPACKET。

    【讨论】:

      猜你喜欢
      • 2013-04-16
      • 2021-12-10
      • 1970-01-01
      • 2016-01-23
      • 2013-02-16
      • 2015-11-09
      • 2012-04-26
      • 2012-02-12
      • 1970-01-01
      相关资源
      最近更新 更多