Please excuse my lack of knowledge regarding Tasks and Async.
Using the TcpClient class I am creating a connection with an available server:
void async RunClientAsync()
{
TcpClient client = new TcpClient();
try
{
await client.ConnectAsync(IPAddress.Parse("1.1.1.1"), 8889);
Task.Start(() => ReadClientAsync(client));
}
catch (Exception ex)
{
HandleException(ex);
}
}
// -----
void async ReadClientAsync(TcpClient client)
{
byte[] bf = new byte[2048];
try
{
while(true)
{
int br = await client.NetworkStream().ReadAsync();
if (br > 0)
{
HandleInboundData(bf, br);
}
}
}
catch (Exception ex)
{
HandleException(ex);
}
}
The helper methods HandleException(Exception ex) and HandleInboundData(byte[] buffer, int length) will perform the assumed tasks.
The connection to the server will be in perpetuity and the data received from the server will be of unknown length and frequency, the idea being to throw a task out there that receives and processes the inbound data only when data is available.
ReadClientAsync(TcpClient client) is an obvious fail because ReadAsync will always return 0 bytes if there is no data available.
How should I approach writing ReadClientAsync using async / task to prevent the busy-looping situation? I've used BeginRead / EndRead in these situations before, which has worked fine. Would that be the solution in this particular case?
Thank you,
Best Answer
No, that's not how TCP works.
NetworkStream
is considered to be in an "end of stream" state when the other side has initiated (possible one-way) shutdown. That's whenReadAsync
(orRead
, for that matter) returns zero - not in any other case.The MSDN documentation can be easily misunderstood - mainly because you're looking at the wrong piece of documentation.
NetworkStream
doesn't overrideReadAsync
(there's no reason to do so), so you're actually looking at the documentation for the genericStream.ReadAsync
. In contrast, the documentation forNetworkStream.Read
says:Note the final sentence, which tells you what it actually means for a
NetworkStream
to be "end of stream". This is how TCP connections are closed.Your response to this should usually be shutting down the connection from the other side as well -
return
out of your helper method and clean up the socket. In any case, do not repeat thewhile (true)
again - you're just going to get an infinite loop that eats 100% of your CPU.If you want a few pointers on how to handle C# asynchronous sockets with
await
, have a look at my sample at https://github.com/Luaancz/Networking/tree/master/Networking%20Part%202. Note the disclaimers - this is in no way production ready. But it does solve a few of the very common mistakes people make when implementing TCP communication.