4

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

但可能还有其他选择吗?

我需要这个来避免在多连接环境中使用线程。

4

5 回答 5

15

我认为您在谈论的是Reactor Pattern。这是一种非常标准的处理大量网络连接而无需线程的方法,并且在多人游戏服务器引擎中非常常见。另一种实现(在 python 中)是扭曲矩阵

基本算法是:

  • 每个套接字都有一个缓冲区
  • 检查哪些套接字已准备好读取(select()、poll() 或只是迭代)
  • 对于每个插座:
    • 调用 recv() 并将内容累积到套接字的缓冲区中,直到 recv 返回 0 或 EWOULDBLOCK 错误
    • 使用缓冲区的内容调用套接字的应用程序级数据处理程序
    • 清除套接字的缓冲区
于 2009-04-30T19:26:47.523 回答
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 不完整,直到我有更多数据才能解析它”,你可以自己缓冲它在该级别,并且在收到完整消息之前不要向上传递数据。

于 2009-03-13T11:09:45.610 回答
1

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

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

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

C 语言要么存在这样的数据结构,要么你必须自己动手。一旦你有了它,然后简单地读取尽可能多的数据并让缓冲区管理诸如溢出之类的问题(例如跨消息帧边界读取字节)并使用标记偏移量来标记你已经处理的字节。

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

于 2009-05-06T11:47:58.980 回答
0

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

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

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

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

于 2009-05-04T07:14:39.230 回答
0

Ryan Dahl 的 evcom 库,完全符合您的要求。

我在工作中使用它,效果很好。但请注意,它(还没有,但即将推出)没有异步 DNS 解析。Ryan 建议为此使用 Michael Tokarev的 udns。我现在正在尝试采用 udns 而不是阻止 getaddrinfo() 。

于 2009-08-06T16:26:56.510 回答