从 socket 中读取数据

13

我需要使用C语言中的read函数从一个AF_UNIX套接字读取数据到一个缓冲区,但我不知道缓冲区的大小。

我认为最好的方式是读取N个字节,直到读取返回0(套接字中没有更多的写入者)。这样做正确吗?有没有办法猜测在套接字上写入的缓冲区大小?

我想套接字是一个特殊的文件。以二进制模式打开文件并获取其大小可以帮助我确定要给缓冲区的正确大小?

我对C语言非常陌生,请注意。

5个回答

22

常见的一种方法是使用ioctl(..)查询套接字的FIONREAD,它将返回可用数据的大小。

int len = 0;
ioctl(sock, FIONREAD, &len);
if (len > 0) {
  len = read(sock, buffer, len);
}

3

一种从套接字中读取未知数量数据的方法,同时避免阻塞的方式是对非阻塞套接字进行轮询以获取数据。

例如:

char buffer[1024];
int ptr = 0;
ssize_t rc;

struct pollfd fd = {
   .fd = sock,
   .events = POLLIN
};

poll(&fd, 1, 0); // Doesn't wait for data to arrive.
while ( fd.revents & POLLIN )
{
   rc = read(sock, buffer + ptr, sizeof(buffer) - ptr);

   if ( rc <= 0 )
      break;

   ptr += rc;
   poll(&fd, 1, 0);
}

printf("Read %d bytes from sock.\n", ptr); 

2

我认为最好的方法是读取N个字节,直到读取返回0(套接字中没有更多的写入器)。这样做正确吗?

0代表EOF,即另一端已关闭连接。如果通信的另一端关闭了连接,则这样做是正确的。

如果连接未关闭(在同一连接上进行了多次传输,具有冗长的协议),那么情况就会变得更加复杂,其行为通常取决于您使用的SOCK_STREAM或SOCK_DGRAM套接字。

Datagram套接字已经由操作系统为您分隔了。

流套接字不分隔消息(所有数据都是一个不透明的字节流),如果需要,在应用程序级别上必须实现它:例如通过在消息头结构中定义一个大小字段或使用定界符(例如'\n'用于单行文本消息)。在第一种情况下,您将首先读取标题,提取长度,并使用长度读取其余部分的消息。在另一种情况下,将流读入部分缓冲区,搜索定界符并从缓冲区提取包括定界符的消息(根据协议,您可能需要保留部分缓冲区,因为可能使用单个recv() / read()接收到多个命令)。

有没有一种方法可以猜测正在写入套接字的缓冲区的大小?

对于流套接字,没有可靠的方法,因为通信的另一端可能仍在写入数据。想象一个非常普遍的情况:套接字缓冲区大小为32K,但要写入128K。写入应用程序将在send() / write()内部阻塞,等待读取应用程序读出数据,从而为下一个块写入数据释放空间。

对于数据报套接字,通常事先知道消息的大小。或者可以尝试(我自己从未这样做过)recvmsg(MSG_PEEK),如果返回的msghdr.msg_flags中有MSG_TRUNC,则尝试增加缓冲区大小。


1

你说得对,如果你不知道输入的大小,可以每次读取一个字节并将其附加到较大的缓冲区中。


1
读取N个字节,直到读取返回0。
是的!
一个额外的细节。如果发送方不关闭连接,套接字将会阻塞而不是返回。当没有可读内容时,非阻塞套接字将返回-1(带有errno == EAGAIN),这是另一种情况。
以二进制模式打开文件并获取大小是否有助于我知道要给缓冲区正确的大小?
不是的。套接字没有大小。假设您在同一连接上发送了两条消息:文件有多长?

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