当只发送一条消息时从接收中获取两条消息
Getting two messages from receive when only one is sent
我编写了一个服务器,在打开与它的连接后应该等待来自客户端的消息:
while(1){
if(recv(mySocket, buffer, 1000, 0) < 1){
continue;
}
printf("Message received: %s", buffer);
}
我用 wireshark 检查了哪些数据包被发送到这个服务器,但是对于每个发送的数据包,都有 2 个 printf 输出。
我现在的问题是我从哪里得到这条额外的消息。
(附加消息是一些随机字节。但每次都一样。)
您对 recv()
行为的明显期望是不合理的。正如@KarolyHorvath 在评论中观察到的那样,流式套接字(其中包括基于 TCP 的套接字)对 "messages" 没有任何意义。特别是,网络数据包不对应于流套接字上的消息。 POSIX 对 recv()
的行为有这样的说法,事实上:
For stream-based sockets, [...] message boundaries shall be ignored.
虽然这更有可能产生组合多个 "messages" 的效果,但它 可以 也意味着单个消息(由单个 send()
调用)被拆分为多个 recv()
调用。它肯定 将 意味着如果您指定给 recv()
的缓冲区长度小于套接字上实际接收的字节数,但在其他情况下,该结果可能也能得到。
成功时,recv()
returns 复制到接收缓冲区的字节数。如果您真的想实现某种 "message" 交换,那么您可以使用它来帮助您在消息边界上拆分传入数据。但是,请务必认识到,这构成了在流之上实现消息传递协议,因此发送方和接收方需要合作,至少是隐式合作,才能使其正常工作。
John Bollinger 的回答是准确的,并提供了关于您应该如何创建可靠的客户端/服务器应用程序的见解。
关于你的问题,还有另一个问题可以解释你看到的实际输出。正如您使用 wireshark 观察到的那样,数据包很可能是在单个块中发送和接收的。该错误在您的服务器中:您在一个字符数组中接收数据并使用 printf
将其直接打印为字符串。我怀疑数据包不包含终止 '[=12=]'
以使缓冲区成为 "%s"
的正确字符串。 printf
将输出数据包内容加上那里的任何缓冲区内容,直到它到达 '[=12=]'
字节,可能会调用未定义的行为。如果数据包被分割成几个块,您可能会多次看到相同的内容,并且还会出现随机字符。
以下是您应该如何修复代码:
char buffer[2000];
...
for (;;) {
ssize_t count = recv(mySocket, buffer, 1999, 0);
if (count >= 1) {
buffer[count] = '[=10=]';
printf("Message received: |%s|", buffer);
}
}
请注意,缓冲区必须至少比最大数据包大小长 1 个字节,并且此跟踪方法无法处理数据包中嵌入的 '[=12=]'
个字节。
当然,在客户端和服务器之间的传输过程中,数据包可以被切分,所以你必须适当地处理这个问题,以实现一个合适的协议。
我编写了一个服务器,在打开与它的连接后应该等待来自客户端的消息:
while(1){
if(recv(mySocket, buffer, 1000, 0) < 1){
continue;
}
printf("Message received: %s", buffer);
}
我用 wireshark 检查了哪些数据包被发送到这个服务器,但是对于每个发送的数据包,都有 2 个 printf 输出。
我现在的问题是我从哪里得到这条额外的消息。
(附加消息是一些随机字节。但每次都一样。)
您对 recv()
行为的明显期望是不合理的。正如@KarolyHorvath 在评论中观察到的那样,流式套接字(其中包括基于 TCP 的套接字)对 "messages" 没有任何意义。特别是,网络数据包不对应于流套接字上的消息。 POSIX 对 recv()
的行为有这样的说法,事实上:
For stream-based sockets, [...] message boundaries shall be ignored.
虽然这更有可能产生组合多个 "messages" 的效果,但它 可以 也意味着单个消息(由单个 send()
调用)被拆分为多个 recv()
调用。它肯定 将 意味着如果您指定给 recv()
的缓冲区长度小于套接字上实际接收的字节数,但在其他情况下,该结果可能也能得到。
成功时,recv()
returns 复制到接收缓冲区的字节数。如果您真的想实现某种 "message" 交换,那么您可以使用它来帮助您在消息边界上拆分传入数据。但是,请务必认识到,这构成了在流之上实现消息传递协议,因此发送方和接收方需要合作,至少是隐式合作,才能使其正常工作。
John Bollinger 的回答是准确的,并提供了关于您应该如何创建可靠的客户端/服务器应用程序的见解。
关于你的问题,还有另一个问题可以解释你看到的实际输出。正如您使用 wireshark 观察到的那样,数据包很可能是在单个块中发送和接收的。该错误在您的服务器中:您在一个字符数组中接收数据并使用 printf
将其直接打印为字符串。我怀疑数据包不包含终止 '[=12=]'
以使缓冲区成为 "%s"
的正确字符串。 printf
将输出数据包内容加上那里的任何缓冲区内容,直到它到达 '[=12=]'
字节,可能会调用未定义的行为。如果数据包被分割成几个块,您可能会多次看到相同的内容,并且还会出现随机字符。
以下是您应该如何修复代码:
char buffer[2000];
...
for (;;) {
ssize_t count = recv(mySocket, buffer, 1999, 0);
if (count >= 1) {
buffer[count] = '[=10=]';
printf("Message received: |%s|", buffer);
}
}
请注意,缓冲区必须至少比最大数据包大小长 1 个字节,并且此跟踪方法无法处理数据包中嵌入的 '[=12=]'
个字节。
当然,在客户端和服务器之间的传输过程中,数据包可以被切分,所以你必须适当地处理这个问题,以实现一个合适的协议。