关于recv和读缓冲区 - C Berkeley套接字

时间:2010-12-06 01:37:21

标签: c++ c sockets timeout berkeley-sockets

我正在使用berkeley套接字和TCP(SOCK_STREAM套接字)。

过程是:

  1. 我连接到远程地址。
  2. 我发了一条消息。
  3. 我收到了一条消息。
  4. 想象一下,我正在使用以下缓冲区:

    char recv_buffer[3000];
    recv(socket, recv_buffer, 3000, 0);
    

    问题是:

    • 如何在第一次调用recv后读取缓冲区是否为空?如果它不是空的,我将不得不再次调用recv,但如果我在空的时候这样做,我会让它阻塞很长时间。
    • 我怎么知道我已经将多少字节重新加入recv_buffer?我不能使用strlen,因为我收到的消息可能包含空字节。

    感谢。

4 个答案:

答案 0 :(得分:12)

  

如何在致电recv之后知道   第一次读缓冲区为空或   不?如果它不是空的我将不得不   再次打电话给recv,但如果我这样做的话   它是空的我会阻止它   很长一段时间。

您可以使用selectpoll系统调用以及套接字描述符来判断是否有数据等待从套接字读取。

但是,通常应该有一个商定的协议,发送方和接收方都遵循该协议,以便双方知道要传输多少数据。例如,发送方可能首先发送一个2字节的整数,表示它将发送的字节数。接收器然后首先读取这个2字节的整数,以便它知道从套接字读取多少字节。

无论如何,正如Tony在下面指出的那样,强大的应用程序应该在标头中使用长度信息的组合,并在每次调用recv之前轮询套接字以获取其他数据,(或使用非阻塞插座)。这将阻止您的应用程序阻止,例如,您知道(从标题)仍然有100个字节要读取,但对等方无法出于任何原因发送数据(也许对等计算机是意外关闭),从而导致recv来电阻止。

  

我怎么知道我有多少字节   进入recv_buffer?我不能用   strlen因为我收到的消息   可以包含空字节。

recv系统调用将返回读取的字节数,如果发生错误则返回-1。

来自recv(2)的手册页:

  

[recv]返回字节数   收到,如果发生错误,则返回-1。   当返回值为0时   同行已经有条不紊地进行了   关机。

答案 1 :(得分:2)

  

如何在第一次调用recv后读取缓冲区是否为空?

即使是第一次(在接受客户端之后),如果客户端连接丢失,recv也会阻塞并失败。你必须:

  • 使用selectpoll(BSD套接字)或某些特定于操作系统的等效项,它可以告诉您特定套接字描述符上是否有可用数据(以及异常条件和缓冲区空间)可写更多输出到)
  • 您可以将套接字设置为非阻塞,这样recv只会返回任何可立即使用的内容(可能没有)
  • 你可以创建一个你可以承受阻止recv数据的线程,知道其他线程会做你还在继续的其他工作
  

我如何知道我已经将多少字节重新加入recv_buffer?我不能使用strlen,因为我收到的消息可以包含空字节。

recv()返回读取的字节数,或者出错时为-1。

请注意,TCP是字节流协议,这意味着您只能保证能够以正确的顺序从中读取和写入字节,但不保证消息边界保存。因此,即使发送方对其套接字进行了大量单个写入,它也可以在路由中分段并到达几个较小的块,或者可以合并和检索几个较小的send() / write()。一个recv() / read()

出于这个原因,请确保循环调用recv,直到获得所需的所有数据(即可以处理的完整逻辑消息)或错误。您应该准备好/能够处理从您的客户端获取部分/全部后续send的内容(如果您没有协议,每方只有在收到另一方的完整消息后才发送,并且未使用带有消息长度的标头)。请注意,对邮件标题执行recvs(有长度),然后正文可能会导致对recv()的更多调用,从而对性能产生潜在的不利影响。

这些可靠性问题经常被忽略。当它们在单个主机上时,它们表现得更少,可靠且快速的LAN,涉及的路由器和交换机更少,以及更少或非并发消息。然后他们可能会在负载和更复杂的网络中崩溃。

答案 2 :(得分:0)

  1. 如果recv()返回少于3000个字节,则可以假设读取缓冲区为空。如果它在3000字节缓冲区中返回3000个字节,那么您最好知道是否继续。大多数协议包括TLV的一些变化 - 类型,长度,值。每条消息都包含一条消息类型的指示符,一些长度(如果长度固定,可能由类型暗示)和值。如果在读取您收到的数据时,您发现最后一个单元不完整,您可以假设还有更多要读取的单元。您还可以将套接字转换为非阻塞套接字;如果没有读取数据,那么recv()将失败并使用EAGAIN或EWOULDBLOCK。

  2. recv()函数返回读取的字节数。

答案 3 :(得分:0)

带有FIONREAD选项的ioctl()会告诉您当前可以无阻塞地读取多少数据。