Socket编程:recv()

4
我有一个应用程序,其中各个实体通过套接字进行通信,我使用C编程语言。当一个实体向另一个实体发送一条长消息时,recv()函数可能会分段读取此消息。因此,我需要在接收方将所有接收到的部分附加在一起来重构消息。
我的问题是关于recv()的一般套接字编程问题。recv()如何知道何时完全读取一条消息?我应该以特殊字符"\n"终止消息吗?还是应该将消息大小作为标头发送?什么是常见做法?
3个回答

8
正如您所注意到的那样,使用流套接字时没有内置的消息边界概念。您需要构建某种确定消息结束的方式到您的应用程序级协议中。
您提出的两个选项都很常见:长度前缀(每个消息以消息长度开头)或消息结束定界符(例如,在基于文本的协议中可能只是一个换行符)。第三个不太常用的选项是强制每个消息具有固定大小。这些选项的组合也是可能的 - 例如,包括长度值的固定大小标题。

谢谢...了解常规做法确实有所帮助。 - NewToAndroid

3
当您使用send()和recv()时,需要指定缓冲区大小。
如果您正在使用此方法发送消息:
send(new_socket,message,strlen(message),0);

第三个参数是您缓冲区的大小。

如果您正在使用TCP套接字,则了解您是否成功发送数据包的一种方法是使用send()recv()函数返回相同的值。在发送方检查消息大小是否与send()返回的值相同即可。

在接收方进行检查的最简单的方法是向字符串添加字符串结束符\0


发送分隔符,使用 strlen(message)+1 就可以了。 - glglgl
除此之外,最好谈论“发送方”。从连接开始,双方都可以发送和接收,因此在这里使用术语“服务器”是极其误导的。 - glglgl
@glglgl 谢谢,我已经将服务器和客户端改为发送方和接收方。此外,我假设字符串已经有了\0分隔符,因此它已经包含在字符串的长度中,但如果字符串没有分隔符,那么你是正确的。 - Sarp Kaya
send()和recv()返回相同的值没有任何理由。TCP是一个字节流协议,而不是消息协议。如果您想要消息边界,您必须自己实现它们。例如,Recv()完全有权每次返回一个字节。-1 - user207421

0

一旦开始在C语言中进行大量的网络编程,人们很快就会意识到为什么高级语言如此受欢迎!基本上,它们内置了许多功能,你很快就会发现自己希望C语言也能提供更多的功能!

首先,我强烈建议您查看ZeroMQ(http://zeromq.org/bindings:c)及其C绑定。这个库可以为您处理连接、消息分隔等许多繁琐的工作。而且,它运行速度快,开发和运行都很快,这是一个好库的标志。

ZeroMQ几乎是完美的套接字库。唯一它还没有做到的事情(据我所知)是主动监视连接以查看是否已经断开 - 只有在尝试发送数据时才会发现。如果您想要检查连接的健康状况,您需要定期发送自己的连接测试消息。

其次,我建议您考虑序列化。一旦您开始拥有指向分配内存的复杂数据结构,您将开始进入复杂和困难的领域。面对这个问题时,我选择使用ASN.1来定义和序列化我的数据结构,使用Objective Systems(http://www.obj-sys.com/index.php)的库和工具。它需要花费一些钱,并需要一点时间来适应,但我发现在开发中节省了大量时间,非常值得。
除了序列化例程外,他们还提供了一些C语言没有的非常方便的额外功能。例如,他们的代码生成器将为您提供复制数据类型的例程,如果该数据类型是一个包含引用分配内存的指针的结构,则非常方便。
可能也有一些免费的工具和库。一个很好的替代方案是Google的协议缓冲区,它有一个C绑定(http://code.google.com/p/protobuf-c/)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接