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,
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 when ReadAsync
(or Read
, 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 override ReadAsync
(there's no reason to do so), so you're actually looking at the documentation for the generic Stream.ReadAsync
. In contrast, the documentation for NetworkStream.Read
says:
This method reads data into the buffer parameter and returns the number of bytes successfully read. If no data is available for reading, the Read method returns 0. The Read operation reads as much data as is available, up to the number of bytes specified by the size parameter. If the remote host shuts down the connection, and all available data has been received, the Read method completes immediately and return zero bytes.
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 the while (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.