阻止读取器连接到命名管道

时间:2014-01-19 22:48:16

标签: c linux fifo

我已经编写了一个服务器,它打开一个命名管道(阻塞,所以它等待客户端连接),然后定期写入管道。客户端打开管道,从中读取并处理数据。但是,由于我无法控制的情况,客户经常退出并很快再次重新启动。

当服务器想要在没有读取器连接到管道的短时间间隔内写入内容时,这会导致问题:服务器收到SIGPIPE并退出。我可以忽略该信号,但我不想丢失数据:理想情况下,服务器会等到客户端在写入数据之前重新连接到管道。在写入期间服务器阻塞没问题。

使用write()我可以尝试0字节写入并检查EPIPE错误以检测是否连接了客户端。但是我怎么能阻止直到连接客户端(除了睡了一下,再次尝试写入)?

还是有另一个更好的方法来实现这个目标吗?

1 个答案:

答案 0 :(得分:2)

事实证明,与我对上述原始问题的评论相反,有一个直截了当的解决方案。

此解决方案假定所有读取器和同一FIFO的所有写入器共享内核缓冲区。这应该是实现FIFO的最合乎逻辑和最简单的方法(考虑到它们的行为),所以我确实所有提供FIFO的系统都采用这种方式。但是,这只是我的假设,而不是任何保证。我没有在相关的POSIX标准中找到任何支持或反驳这一点的内容。如果您发现其他情况,请填写。

程序很简单:

当客户端意外消失时,编写器再次打开FIFO,而不先关闭原始描述符。此open()将阻止,直到有新的阅读器可用,但由于原始文件描述符仍处于打开状态,因此FIFO中已缓冲的数据将可供新阅读器使用。如果open()成功,则编写器只关闭原始描述符,然后切换到使用新描述符。

如果内核结构是共享的,则FIFO缓冲区状态在写入器描述符之间共享,新读者将能够读取前一个读取器未读取的内容。

(请注意,编写器不知道客户端更改之间缓冲的数据量,因此不知道切换发生的数据流中的点。)

我已经验证了这个简单的策略适用于Ubuntu中x86_64上的Linux 3.8.0-35-generic内核,以及x86_64上的2.6.9-104.ELsmp。


但是,我仍然完全同意接受数据丢失或更改协议,正如Basile Starynkevitch在对原始问题的评论中所建议的那样。

就个人而言,我发现Unix域套接字(绑定到路径名,比如说/var/run/yourservice/unix)是一个更好的选择,因为它允许多个同时发生的客户端没有数据损坏(与FIFO不同),以及更加理智的协议

我更喜欢Unix数据报套接字,每个数据报的开头都有一个序列号和数据报长度。 (长度有助于客户端验证它读取整个数据报;我真的不希望任何操作系统截断Unix数据报。)

通常,编写器会向每个客户端发送一些数据报,并在发送新数据报之前等待确认。在处理数据报之后,客户端通过向编写器发送序列号来确认数据报。 (请记住,这些是套接字,因此通信是双向的。)这允许编写器在每个客户端保留一些数据报,并且客户端以正确(序列号)顺序或无序处理数据报。 ,使用多个线程。

重要的一点是,每个数据报只有在处理完毕后才会得到确认,而不是在收到后立即得到确认。

(除了reader-> writer“acks”(确认)之外,我还支持“请重新发送”响应,以防客户端使用太小的缓冲区来接收数据报。或者将其丢弃在地板上由于其他一些原因。甚至可能对数据报“抱怨”,客户端也不知道该怎么做。)

如果客户端消失,编写者知道所有未确认的数据报尚未由客户端处理,并且可以将它们重新发送到另一个连接的客户端或未来的客户端。

有问题吗?