2013-01-11 32 views
3

我有一个可能没有问题的问题要问,我环顾四周,但还没有看到解决它的直接答案,并认为我可能会在这里得到一个快速的答案。在一个使用bsd套接字的简单的TCP/IP客户端 - 服务器选择循环中,如果客户端发送两个同时到达服务器的消息,那么在服务器上调用recv会返回绑定在缓冲区中的两个消息还是recv强制每个不同的到达消息要单独阅读?C++ posix sockets recv功能

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

回答

7

TCP/IP是基于流的传输,而不是基于数据报的传输。在流中,send()recv()之间不存在1对1的相关性。这仅适用于数据报。所以,你必须做好准备,以处理多种可能性:

  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()以读取下一个消息长度/标题,依此类推。

+0

非常感谢您的详细回复,非常感谢。了解TCP/IP实现的基本细节有助于达成一致,并希望能够帮助那些不了解的人。 – user1930581

1

对于两个消息在单个recv呼叫中返回(参见Nagle's Algorithm)绝对有效。 TCP/IP保证顺序(消息中的字节不会混合)。除了它们在单次调用中一起返回外,单条消息也可能需要多次调用recv(尽管对于所描述的小包来说不太可能)。

+0

谢谢你的直接回应,非常感谢。猜猜我将不得不重做我的代码来解决这个问题! :P – user1930581

+0

我会说“TCP保证订单”。 IP对此无能为力。 – ysdx

0

您唯一能指望的就是字节的顺序。你不能指望他们如何分成recv呼叫。有时候,事情会在终端或者一路上合并。事情也可能在一路上分崩离析,因此独立抵达。这听起来像你的发件人交替发送12和915,但你不能指望它。

+0

感谢您提供易于理解的答案和建议,我将重新编写我的代码来解决此问题。 – user1930581