Posix PIPE 上的非阻塞读取和阻塞写入

Posted

技术标签:

【中文标题】Posix PIPE 上的非阻塞读取和阻塞写入【英文标题】:Non blocking read and blocking write on Posix PIPE 【发布时间】:2013-05-26 08:22:46 【问题描述】:

我正在编写使用 posix FIFO 进行通信的客户端-服务器应用程序。客户端和服务器都是单线程单进程应用程序。

服务器设计用于处理多个客户端。每个客户端都有自己的一对命名管道,一个用于从服务器向该客户端发送消息,另一个用于从客户端向服务器发送消息。

这个想法很简单,Server 循环遍历所有 Client 到 Server 管道并检查那里是否有要读取的内容。

我的第一个实现是这样的:

/* SERVER */
int desc = open(pipeName, O_RDONLY | O_NDELAY); //1
assert(desc > 0); //just simplyfing error handling
int read = read(desc, buffer, BUFSIZE); //2
if(read > 0)
  do_stuff();
close(desc); //3

/* CLIENT */
int desc = open(pipeName, O_WRONLY) //4
assert(desc > 0); //just simplyfing error handling
int read = write(desc, buffer, BUFSIZE) //5
close(desc); //6

据我所知,此代码无效。

存在竞争条件,例如调用顺序如下:1、2、4、3、5、6 - 可能会导致 SIGPIPE。

问题在于,即使 PIPE 的另一端没有写入器,非阻塞读取打开总是成功的。 这意味着如果客户端将在 open() 上阻塞,则服务器执行非阻塞打开(这将解锁客户端),然后 read() 将返回 0,因为此时 PIPE 和之后的 close() 中将没有任何内容,那么当控制将返回想要在打开的 PIPE 上执行 write() 的客户端,这将导致 SIGPIPE,因为阅读器不再可用(服务器已经关闭了管道)。

目前我看到了两种解决方案:

    使用“tryAgain()”在客户端处理 SIGPIPE - 这看起来很糟糕,如果我这样做,则无法保证它在任何时候都能正常工作 - 这取决于良好的命令处理顺序的可能性... 继续阅读 PIPE 在服务器中一直打开(打开一次并在连接被视为完成时关闭)- 这在我的应用程序架构中很不方便,但当然可以。我想它会解决问题,但我不确定。

这是我的问题:

    这两种方法是否正确? 还有其他方法可以处理这种情况吗? 您认为第二种解决方案能正常工作吗? 您会选择什么?为什么?

感谢您的每一个回答。

【问题讨论】:

OT:应该是assert(desc != -1);assert(desc >= 0);,因为open() 在错误时返回-1,在成功时返回非负整数。 不要编辑海报问题/代码。评论或回答错误的原因。 当你说它应该处理多个客户端时,你的意思是同时吗?因为您的代码一次只处理一个。处理 sigpipe 或“在服务器中一直打开读取管道”的任何更改都不会使其同时处理多个连接。 嗯,不是同时,但我希望它给人留下这样的印象。我的意思是 - 它可以遍历所有客户端并始终尽快处理准备好的客户端。 对那个编辑感到抱歉。你是对的。 【参考方案1】:

我很难理解您的问题。事情

客户端将在 open() 上阻塞

使用 select 或 epoll 来确定哪些文件描述符是可读可写的。

然后,只需对这些调用 read。

您将被通知客户端关闭管道作为读取事件,您需要处理而不是在关闭时写入。

http://linux.die.net/man/4/epoll

https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/

【讨论】:

服务器可以通过轮询来找出哪个客户端想要写一些东西,并且它可能会起作用(我的意思是它会发现客户端正在打开一些管道进行写并等待阅读器开始写信给那些管道)。但我不确定它是否会改变问题。如果服务器发现有客户端想要写东西,它会打开然后用 NODELAY 标志读取它会导致它读取 0 字节然后关闭(服务器不能等待数据,因为它只是一个线程!)。 这意味着客户端将遇到 sam 问题 - 它的打开指令将被解锁,但当进程到达写入时,另一侧将没有读取器(已经关闭),它会导致 SIGPIPE。还是我错了? 你错了,因为如果你轮询正确,你会被告知对方已经终止了连接,所以你可以知道不要在上面调用 write。 嗯,那我今天就试着这样实现。我想我的 C++ OO 代码中的系统通知处理可能存在一些问题,但我想没有什么是不可能处理的——它看起来很糟糕;)谢谢你的回答。

以上是关于Posix PIPE 上的非阻塞读取和阻塞写入的主要内容,如果未能解决你的问题,请参考以下文章

Linux pipe():从管道读取并不总是解除对写入器的阻塞

阻塞和非阻塞子进程调用

线程和分叉:从 popen()-pipe 读取时 fgetc() 阻塞

PHP中管道的非阻塞打开

在 Python 中对 subprocess.PIPE 进行非阻塞读取

在 Python 中对 subprocess.PIPE 进行非阻塞读取