Domanda

I've written a server which opens a named pipe (which blocks, so it waits until a client is connected), and then periodically writes something to the pipe. The client opens the pipe, reads from it and processes the data. However, due to circumstances I can't control, the client frequently exits and is restarted again soon thereafter.

This causes problems when the server wants to write something in the short interval when there is no reader connected to the pipe: the server receives a SIGPIPE and exits. I can ignore the signal, but I don't want to lose the data: ideally, the server would wait until the client has reconnected to the pipe before writing the data. It's no problem for the server to block during this write.

Using write() I can try a 0-byte write and check for a EPIPE error to detect whether there is a client connected. But how can I block until a client is connected (aside from sleeping a bit, and trying the write again)?

Or is there another, better way to achieve this?

È stato utile?

Soluzione

It turns out that contrary to my comment to the original question above, there is a straightforward solution.

This solution assumes that all readers and all writers to the same FIFO share the kernel buffers. This should be the most logical and straightforward way to implement FIFOs (considering their behaviour), so I do expect all systems providing FIFOs to behave this way. However, it is just my assumption, not any guarantee. I have not found anything in the relevant POSIX standards to support or contradict this. Please do pipe up if you find otherwise.

The procedure is trivial:

When a client vanishes unexpectedly, the writer opens the FIFO again, without closing the original descriptor first. This open() will block, until there is a new reader available, but since the original file descriptor is still open, the data already buffered in the FIFO will be available to the new reader. If the open() succeeds, the writer simply closes the original descriptor, and switches to using the new descriptor instead.

If the kernel structures are shared, the FIFO buffer state is shared between the writer descriptors, and the new reader will be able to read what the previous reader left unread.

(Note that the writer does not know the amount of data buffered between client changes, and is therefore unaware of the point in data stream where the switch happens.)

I have verified this trivial strategy works on Linux 3.8.0-35-generic kernels on x86_64 in Ubuntu, as well as 2.6.9-104.ELsmp on x86_64.


However, I still fully agree with either accepting the data loss, or changing the protocol, as suggested by Basile Starynkevitch in a comment to the original question.

Personally, I have found Unix domain sockets (bound to a pathname, say /var/run/yourservice/unix) to be a much better option, because it allows multiple simultaneous clients without data corruption (unlike FIFOs), and a much saner protocol.

I prefer Unix datagram sockets, with a sequence number and datagram length at the start of each datagram. (The length helps the client verify it read the entire datagram; I don't really expect any OS to truncate Unix datagrams.)

Typically, the writer sends a few datagrams to each client, and waits for acknowledgement before sending new ones. After processing a datagram, the client acknowledges the datagram by sending the sequence number to the writer. (Remember, these are sockets, so the communication is bidirectional.) This allows the writer to keep a few datagrams in flight per client, and the client to process the datagrams in either correct (sequence number) order, or out-of-order, using multiple threads.

The important point being that each datagram is acknowledged only after it has been processed, not immediately after it has been received.

(In addition to reader->writer "acks" (acknowledgements), I would also support "please resend" responses, in case the client used a too small buffer to receive a datagram. Or dropped it on the floor due to some other reasons. And maybe even "yuck" for datagrams the client does not know what to do about.)

If a client vanishes, the writer knows that all un-acknowledged datagrams were not processed by the client yet, and can re-send them to another connected client, or a future client.

Questions?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top