我正在使用berkeley套接字和TCP(SOCK_STREAM套接字)。
过程是:
想象一下,我正在使用以下缓冲区:
char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
问题是:
感谢。
答案 0 :(得分:12)
如何在致电recv之后知道 第一次读缓冲区为空或 不?如果它不是空的我将不得不 再次打电话给recv,但如果我这样做的话 它是空的我会阻止它 很长一段时间。
您可以使用select
或poll
系统调用以及套接字描述符来判断是否有数据等待从套接字读取。
但是,通常应该有一个商定的协议,发送方和接收方都遵循该协议,以便双方知道要传输多少数据。例如,发送方可能首先发送一个2字节的整数,表示它将发送的字节数。接收器然后首先读取这个2字节的整数,以便它知道从套接字读取多少字节。
无论如何,正如Tony在下面指出的那样,强大的应用程序应该在标头中使用长度信息的组合,并在每次调用recv
之前轮询套接字以获取其他数据,(或使用非阻塞插座)。这将阻止您的应用程序阻止,例如,您知道(从标题)仍然有100个字节要读取,但对等方无法出于任何原因发送数据(也许对等计算机是意外关闭),从而导致recv
来电阻止。
我怎么知道我有多少字节 进入recv_buffer?我不能用 strlen因为我收到的消息 可以包含空字节。
recv
系统调用将返回读取的字节数,如果发生错误则返回-1。
来自recv(2)的手册页:
[recv]返回字节数 收到,如果发生错误,则返回-1。 当返回值为0时 同行已经有条不紊地进行了 关机。
答案 1 :(得分:2)
如何在第一次调用recv后读取缓冲区是否为空?
即使是第一次(在接受客户端之后),如果客户端连接丢失,recv也会阻塞并失败。你必须:
select
或poll
(BSD套接字)或某些特定于操作系统的等效项,它可以告诉您特定套接字描述符上是否有可用数据(以及异常条件和缓冲区空间)可写更多输出到)recv
只会返回任何可立即使用的内容(可能没有)recv
数据的线程,知道其他线程会做你还在继续的其他工作我如何知道我已经将多少字节重新加入recv_buffer?我不能使用strlen,因为我收到的消息可以包含空字节。
recv()
返回读取的字节数,或者出错时为-1。
请注意,TCP是字节流协议,这意味着您只能保证能够以正确的顺序从中读取和写入字节,但不保证消息边界保存。因此,即使发送方对其套接字进行了大量单个写入,它也可以在路由中分段并到达几个较小的块,或者可以合并和检索几个较小的send()
/ write()
。一个recv()
/ read()
。
出于这个原因,请确保循环调用recv
,直到获得所需的所有数据(即可以处理的完整逻辑消息)或错误。您应该准备好/能够处理从您的客户端获取部分/全部后续send
的内容(如果您没有协议,每方只有在收到另一方的完整消息后才发送,并且未使用带有消息长度的标头)。请注意,对邮件标题执行recvs(有长度),然后正文可能会导致对recv()
的更多调用,从而对性能产生潜在的不利影响。
这些可靠性问题经常被忽略。当它们在单个主机上时,它们表现得更少,可靠且快速的LAN,涉及的路由器和交换机更少,以及更少或非并发消息。然后他们可能会在负载和更复杂的网络中崩溃。
答案 2 :(得分:0)
如果recv()
返回少于3000个字节,则可以假设读取缓冲区为空。如果它在3000字节缓冲区中返回3000个字节,那么您最好知道是否继续。大多数协议包括TLV的一些变化 - 类型,长度,值。每条消息都包含一条消息类型的指示符,一些长度(如果长度固定,可能由类型暗示)和值。如果在读取您收到的数据时,您发现最后一个单元不完整,您可以假设还有更多要读取的单元。您还可以将套接字转换为非阻塞套接字;如果没有读取数据,那么recv()
将失败并使用EAGAIN或EWOULDBLOCK。
recv()
函数返回读取的字节数。
答案 3 :(得分:0)
带有FIONREAD选项的ioctl()会告诉您当前可以无阻塞地读取多少数据。