仅发送一条消息时从receive获取两条消息
我编写了一个服务器,在打开与客户端的连接后,它应该等待来自客户端的消息:仅发送一条消息时从receive获取两条消息,c,sockets,networking,C,Sockets,Networking,我编写了一个服务器,在打开与客户端的连接后,它应该等待来自客户端的消息: while(1){ if(recv(mySocket, buffer, 1000, 0) < 1){ continue; } printf("Message received: %s", buffer); } while(1){ if(recv(mySocket,buffer,1000,0)因此recv也返回两次,但是客户端只发送一个packet将recv的返回值保存在一个变量中,并每次
while(1){
if(recv(mySocket, buffer, 1000, 0) < 1){
continue;
}
printf("Message received: %s", buffer);
}
while(1){
if(recv(mySocket,buffer,1000,0)<1){
继续;
}
printf(“收到的消息:%s”,缓冲区);
}
我向wireshark检查了哪些数据包被发送到此服务器,但对于每个发送的数据包,都有2个printf输出
我现在的问题是,我从哪里得到这个额外的信息
(附加的消息是一些随机字节。但每次都是一样的。)您对
recv()
行为的明显期望是没有道理的。正如@KarolyHorvath在评论中所观察到的,流套接字(其中包括基于TCP的套接字)对“消息”没有任何意义。特别是,网络数据包不对应于流套接字上的消息。POSIX对recv()
的行为有这样的描述,事实上:
对于基于流的套接字,[…]消息边界应被忽略
虽然这更有可能产生组合多个“消息”的效果,但也可能意味着单个消息(由单个send()
调用发送)被拆分为多个recv()
调用。当然,将意味着如果指定给recv()
的缓冲区长度小于套接字上实际接收的字节数,但也有其他情况可以获得该结果
成功时,
recv()
返回复制到接收缓冲区的字节数。如果您真的试图实现某种“消息”交换,那么您可以使用它来帮助您在消息边界上分割传入数据。但是,请务必认识到,这构成了在流的顶部实现消息传递协议,因此发送方和接收方需要进行合作(至少是隐式合作),以使其正常工作。John Bollinger的回答是准确的,并为您提供了创建可靠的客户机/服务器应用程序应采取的措施的见解
关于您的问题,还有一个问题可以解释您看到的实际输出。正如您使用wireshark观察到的那样,数据包很可能在单个数据块中发送和接收。错误在您的服务器中:您接收字符数组中的数据,并使用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'
字节
当然,数据包可以在客户端和服务器之间进行切片和分块,因此您必须适当地处理此问题以实现适当的协议。您收到了两个数据包吗?@Ed Heal Well printf被称为两次->因此recv也返回两次,但是客户端只发送一个packet将recv的返回值保存在一个变量中,并每次打印它。如果为零,则您没有收到消息,因此不应打印缓冲区。@jarmod I将If(recv(mySocket,buffer,1000,0)=-1)更改为If(recv(mySocket,buffer,1000,0)<1),但没有任何更改。这是什么类型的套接字?数据包的长度是多少?我想还有更多,你的答案几乎肯定是不完整的,但我正在等待OP的回应……你是对的。数据包被拆分,因为接收缓冲区的大小只有几个字节。@scaletos,我很高兴你发现了这个问题。但是,我重申,通过TCP连接的数据包数量及其有效负载的大小在很大程度上是
recv()
附带的。您可能会发现,接收到足够大的缓冲区通常允许您逐个数据包读取数据包,但您不能依靠通过send()
发送的“消息”与数据包以1:1的比例对应,也不能依靠通过这种连接接收的数据包与recv()接收的“消息”以1:1的比例对应
。谢谢,但我的服务器只用于打印小于1000字节(包括空字节)的邮件。@scaleos:是否将'\0'
字节作为数据包的一部分传输?