I’m writing a tcp server in Windows NT using completion ports to exploit asynchronous I/O.
I have a TcpSocket class, a TcpServer class and some (virtual functions) callbacks to call when an I/O operation is completed, e.g. onRead() for when a read is completed. I have also onOpen() for when the connection is established and onEof() for when the connection is closed, and so on.
I always have a pending read for the socket, so if the socket effectively gets data (the read will be completed with size > 0) it calls onRead(), instead if the client closes the socket from the client side (the read will be completed with size == 0) it calls onEof(), and the server is aware of when the client closes the socket with closesocket(server_socket); from its side.
All works gracefully, but I have noticed a thing:
when i call closesocket(client_socket); on the server’s side endpoint of the connection, instead of the client side, (either with setting linger {true, 0} or not), the pending read will be completed as erroneous,
that is, the read size will not only be == 0, but also GetLastError() returns an error: 64, or ‘ERROR_NETNAME_DELETED’. I have searched much about this on the web, but didn’t find nothing interesting.
Then I asked myself: but is this a real error? I mean, can this really be considered an error?
The problem is that on the server side, the onError() callback will be called when I closesocket(client_socket); instead of the onEof(). So I thought this:
What about if I, when this ‘ERROR_NETNAME_DELETED’ “error” is received, call onEof() instead of onError() ?
Would that introduce some bugs or undefined behavior?
Another important point that made me ask this question is this:
When I have received this read completion with ‘ERROR_NETNAME_DELETED’, I have checked the OVERLAPPED
structure, in particular the overlapped->Internal parameter which contain the NTSTATUS error code
of the underlying driver. If we see a list of NTSTATUS error codes [ http://www.tenox.tc/links/ntstatus.html ]
we can clearly see that the ‘ERROR_NETNAME_DELETED’ is generated by the NTSTATUS 0xC000013B, which is an error, but it is called ‘STATUS_LOCAL_DISCONNECT’. Well, it doesn’t look like a name for an error. It seems more like `ERROR_IO_PENDING’ which is an error, but also a status for a correct behavior.
So what about checking the OVERLAPPED structure’s Internal parameter, and when this is == to ‘STATUS_LOCAL_DISCONNECT’ a call to the onEof() callback is performed? Would mess things up?
In addition, I have to say that from the server side, if I call DisconnectEx() before calling
closesocket(client_socket); I will not receive that error. But what about I don’t want to call DisconnectEx() ? E.g. when the server is shutting down and doesn’t want to wait all DisconnectEx() completions, but just want to close all client’s connected.
It’s entirely up to you how you treat an error condition. In your case this error condition is entirely to be expected, and it’s perfectly safe for you to treat it as an expected condition.
Another example of this nature is when you call an API function but don’t know how large a buffer to provide. So you provide a buffer that you hope will be big enough. But if the API call fails, you then check that the last error is
ERROR_INSUFFICIENT_BUFFER. That’s an expected error condition. You can then try again with a larger buffer.