In a number of places, MP and Dafilesrv being 2 notable cases, we install select handlers that call back into notifySelected and then check how much data is available via ISocket::avail_read() (which does a ioctl(.. FIONREAD ..) call), and then perform a blocking read. Assuming avail_read is correct this will be non-blocking.
We also treat 0 being available as a graceful close of the socket.
There is some suspicion that this is not best practice and doesn't seem to be the standard approach.
The more standard approach is to perform a non-blocking read on an epoll event and consume whatever it returns. If it returns 0 bytes, then that is treated as notification of graceful close.