I want to use NetworkStream
(or maybe Socket
) to read/write TCP connections. I want to use non-blocking operations so that I don't have to deal with several threads, or deal with the issue of how to stop the program if some threads might be halted on blocking network operations.
The documentation of NetworkStream.Read implies it is non-blocking ("If no data is available for reading, the Read method returns 0"), so I guess I don't need async callbacks for reading... right?
But what about writing? Well, Microsoft has one of its usual low-quality tutorials on this subject, and they propose writing a cumbersome AsyncCallback
method for every operation. What's the point of the code inside this callback?
client.BeginSend(byteData, 0, byteData.Length, SocketFlags.None,
new AsyncCallback(SendCallback), client);
...
private static void SendCallback(IAsyncResult ar)
{
try {
Socket client = (Socket)ar.AsyncState;
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
The documentation of AsyncCallback
says it's "a callback method that is called when the asynchronous operation completes". If the operation is completed already, why do we have to call Socket.EndSend(IAsyncResult)
? Meanwhile, the documentation of NetworkStream.BeginWrite
says "You must create a callback method that implements the AsyncCallback
delegate and pass its name to the BeginWrite
method. At the very minimum, your state parameter must contain the NetworkStream
."
But why "must" I? Can't I just store the IAsyncResult
somewhere...
_sendOp = client.BeginSend(message, 0, message.Length, SocketFlags.None, null, null);
...and periodically check whether it's finished?
if (_sendOp.IsCompleted)
// start sending the next message in the queue
(I know this implies polling, but it will be in a server that is expected to be active most of the time, and I'm planning some Thread.Sleeps when idle.)
Another issue. The messages I'm sending are broken up into a header array and a body array. Can I can issue two BeginWrites in a row?
IAsyncResult ar1 = _stream.BeginWrite(header, 0, header.Length, null, null);
IAsyncResult ar2 = _stream.BeginWrite(msgBody, 0, msgBody.Length, null, null);
Also, any opinions are welcome about whether to use Socket or NetworkStream.
I don't have any experience with NetworkStream
, but I can say this about asynchronous Socket
operations:
End*
needs to be called when the operation completes because this cleans up OS resources used by the asynchronous operation and allows you to get the result of the operation (even if that result is just whether it succeeded or failed). MSDN has a good overview of this common asynchronous pattern.I do recommend using completion delegates instead of polling. If you want an easier-to-use wrapper, you could try Nito.Async.Sockets, which wraps the Begin
/End
operations and serializes them all through a single thread (e.g., a UI thread).
P.S. If you want to stop a blocking operation, you can close the socket from another thread, which will cause the operation to complete with an error. However, I don't recommend using blocking socket operations.