TCP/IP是基于流的传输,而不是基于数据报的传输。在流中,send()
和recv()
之间不存在1对1的相关性。这仅适用于数据报。所以,你必须做好准备,以处理多种可能性:
到send()
单一呼叫可以容纳在一个TCP数据包,并通过向recv()
单个呼叫在完整地阅读。
对send()
的单个调用可能会跨越多个TCP数据包,并且需要多次调用recv()
才能读取所有内容。
对send()
的多次调用可能适合单个TCP数据包,并可通过对recv()
的单次调用完全读取。
对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()
以读取下一个消息长度/标题,依此类推。
非常感谢您的详细回复,非常感谢。了解TCP/IP实现的基本细节有助于达成一致,并希望能够帮助那些不了解的人。 – user1930581