如何缓冲非阻塞IO?

时间:2009-03-12 10:07:04

标签: c io nonblocking buffered

当我在阻塞文件描述符上需要缓冲IO时,我使用stdio。但是如果我根据手动将文件描述符转换为非阻塞模式,则stdio缓冲是不可用的。经过一些研究后,我发现BIO可用于缓冲非阻塞IO。

但可能还有其他选择吗?

我需要这样做以避免在多连接环境中使用线程。

5 个答案:

答案 0 :(得分:15)

我认为你所谈论的是Reactor Pattern。这是一种非线性处理大量网络连接的标准方法,在多人游戏服务器引擎中非常常见。另一个实现(在python中)是twisted matrix

基本算法是:

  • 每个插槽都有一个缓冲区
  • 检查哪些套接字可以读取(select(),poll()或只是迭代)
  • 每个插座的
    • 调用recv()并将内容累积到套接字缓冲区,直到recv返回0或EWOULDBLOCK错误
    • 使用缓冲区的内容调用套接字的应用程序级数据处理程序
    • 清除套接字缓冲区

答案 1 :(得分:3)

我发现现在已经编辑了这个问题,至少比以前更容易理解了。

无论如何,这不是一个矛盾吗?

  • 您可以进行I / O非阻塞,因为您希望能够快速读取少量数据,通常会因延迟而牺牲吞吐量。
  • 你做了缓冲,因为你不关心延迟,但想通过交换吞吐量的延迟来有效利用I / O子系统。

同时做这两件事似乎是一个矛盾,很难想象。

你追求的语义是什么?如果你这样做:

int     fd;
char    buf[1024];
ssize_t got;

fd = setup_non_blocking_io(...);
got = read(fd, buf, sizeof buf);

如果有3个字节可用,您会期望什么行为?阻塞/缓冲I / O可能会阻塞,直到能够读取更多满足您的请求,非阻塞I / O将立即返回3个可用字节。

当然,如果你有一些顶级的协议,它定义了某种消息结构,这样你就可以知道“这个I / O不完整,我无法解析它,直到我有更多的数据”,你可以在那个级别自己缓冲它,并且不会向上传递数据,直到收到完整的消息。

答案 2 :(得分:1)

根据协议的不同,您可能需要为非阻塞网络节点(客户端或服务器)缓冲读取。

通常,这些缓冲区提供多个索引(偏移量),这两个索引都记录处理的最后一个字节的位置和最后一个字节读取(与处理的偏移量相同或更大)。并且它们(应该)提供更丰富的压缩缓冲区,透明缓冲区大小管理等语义。

在Java(至少)中,非阻塞网络io(NIO)包还提供一组数据结构(ByteBuffer等),这些数据结构旨在提供通用数据结构。

C或者存在这样的数据结构,或者你必须自己滚动。一旦你拥有它,然后只需读取尽可能多的数据,让缓冲区管理溢出等问题(例如,跨消息帧边界读取字节),并使用标记偏移来标记已处理的字节。

正如Android指出的那样,您(很可能)需要为每个打开的连接创建匹配的缓冲区。

答案 3 :(得分:0)

您可以为每个打开的文件描述符创建一个包含缓冲区的结构,然后累积这些缓冲区,直到recv()返回0,或者您有足够的数据在缓冲区中处理。

如果我正确理解你的问题,你就无法缓冲,因为非阻塞你正在写入具有多个连接的同一个缓冲区(如果是全局的)或者只是写小块数据(如果是本地的)。

在任何情况下,您的程序必须能够识别数据的来源(可能是文件描述符)并相应地缓冲它。

线程也是一种选择,它并不像许多人听起来那样可怕。

答案 4 :(得分:0)

Ryan Dahl's evcom library完全符合您的要求。

我在工作中使用它并且效果很好。但请注意,它不会(但即将推出)具有异步DNS解析功能。瑞安为此建议udns by Michael Tokarev。我正在尝试采用udns而不是现在阻止getaddrinfo()。