【问题标题】:Getting two messages from receive when only one is sent当只发送一条消息时从接收中获取两条消息
【发布时间】:2015-08-13 23:58:09
【问题描述】:

我编写了一个服务器,它应该在打开与它的连接后等待来自客户端的消息:

while(1){
  if(recv(mySocket, buffer, 1000, 0) < 1){
     continue;
  }   
  printf("Message received: %s", buffer);
}

我用wireshark检查了哪些数据包被发送到这个服务器,但是对于每个发送的数据包都有2个printf输出。

我现在的问题是我从哪里得到这条额外的消息。

(附加消息是一些随机字节。但每次都一样。)

【问题讨论】:

  • 你收到两个包了吗?
  • @Ed Heal Well printf 被调用了两次 -> 所以 recv 也返回了 2 次,但是客户端只发送了一个数据包
  • 将recv的返回值保存在一个变量中,每次都打印出来。如果它为零,那么您确实没有收到一条消息,所以不应该打印缓冲区。
  • @jarmod 我将 if(recv(mySocket, buffer, 1000, 0) == -1) 更改为 if(recv(mySocket, buffer, 1000, 0)
  • 这是什么类型的socket?数据包长度是多少?

标签: c sockets networking


【解决方案1】:

您对recv() 行为的明显期望是不合理的。正如@KarolyHorvath 在 cmets 中观察到的那样,流套接字(基于 TCP 的套接字属于其中)对“消息”毫无意义。特别是,网络数据包与流套接字上的消息不对应。 POSIX 对recv() 的行为有这样的说法,其实:

对于基于流的套接字,应忽略 [...] 消息边界。

虽然这更有可能产生组合多个“消息”的效果,但它可以也意味着单个消息(由单个 send() 调用发送)被拆分为多个 @987654324 @电话。它肯定意味着如果您指定给recv() 的缓冲区长度小于套接字上实际接收到的字节数,但在其他情况下也可以获得该结果。

成功时,recv() 返回复制到接收缓冲区的字节数。如果您真正尝试实现某种“消息”交换,那么您可以使用它来帮助您在消息边界上拆分传入数据。但是,请务必认识到,这构成了在流之上实现消息传递协议,因此发送方和接收方需要合作,至少是隐式合作,才能使其工作。

【讨论】:

  • 我认为还有更多内容,您的回答几乎肯定不完整,但我正在等待 OP 回复...
  • 你是对的。数据包被拆分是因为接收缓冲区的大小只有几个字节。
  • @scaletos,很高兴您发现了这个问题。然而,我重申,通过 TCP 连接的数据包数量及其有效负载的大小在很大程度上与recv() 无关。您可能会发现接收到足够大的缓冲区通常允许您逐个读取数据包,但您不能依赖通过 send() 发送的“消息”与数据包或数据包 1:1 对应在这样的连接上收到与recv() 收到的“消息”1:1 对应。
【解决方案2】:

John Bollinger 的回答是准确的,并提供了有关创建可靠客户端/服务器应用程序应该做什么的见解。

关于您的问题,还有另一个问题可以解释您看到的实际输出。正如您使用wireshark观察到的那样,该数据包很可能以单个块的形式发送和接收。错误在您的服务器中:您在 char 数组中接收数据,并使用printf 将其直接打印为字符串。我怀疑数据包不包含终止'\0',以使缓冲区成为"%s" 的正确字符串。 printf 将输出数据包内容以及任何缓冲区内容,直到它到达'\0' 字节,可能会调用未定义的行为。如果数据包被分成几个块,你可能会多次看到相同的内容,并且还会看到随机字符。

这里是你应该如何修复你的代码:

char buffer[2000];
...
for (;;) {
    ssize_t count = recv(mySocket, buffer, 1999, 0);

    if (count >= 1) {
        buffer[count] = '\0';
        printf("Message received: |%s|", buffer);
    }
}

请注意,缓冲区必须至少比最大数据包大小长 1 个字节,并且此跟踪方法无法处理数据包中嵌入的 '\0' 字节。

当然,数据包可以在客户端和服务器之间的途中被切片和切块,因此您必须适当地处理这一点以实现适当的协议。

【讨论】:

  • 谢谢,但我的服务器仅用于打印小于 1000 字节的消息,包括空字节。
  • @scaleos:您是否将'\0' 字节作为数据包的一部分进行传输?
猜你喜欢
  • 2019-11-08
  • 2017-09-17
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2021-08-06
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多