Task asynchronous pattern with sockets - c#

I am trying to implement a class that can listen for incoming TCP data. I am trying to do so using tasks.
I have two central methods
private async void ReceiveAsync()
{
while (true)
{
int bytesRead = await Receive();
if (bytesRead > 0)
{
byte[] result = new byte[bytesRead];
Buffer.BlockCopy(_buffer, 0, result, 0, bytesRead);
Console.WriteLine(Encoding.UTF8.GetString(result));
}
}
}
and
private Task<int> Receive()
{
return Task.Run(() =>
{
if (sock.Poll(-1, SelectMode.SelectRead))
if (sock.Available > 0)
return sock.Receive(_buffer, _buffer.Length, SocketFlags.None);
return -1;
}
);
}
In my main program, I call ReceiveAsync() and then send some data down the TCP pipeline, to which the receiver responds. I do get this reply, but now I am caught in an endless loop inside the while(true), and further data sent from the "receiver" is not being received.
There is something completely wrong. What is it?

Try:
int bytesRead;
do {
bytesRead = await Receive();
if(bytesRead > 0) {...}
} while (bytesRead > 0);
i.e. use bytesRead as an exit condition.
Also: you can probably use async here very nicely:
// for illustration ONLY
TaskCompletionSource<int> source = new TaskCompletionSource<int>();
sock.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ar =>
{
try
{
int val = ((Socket)ar.AsyncState).EndReceive(ar);
source.SetResult(val);
}
catch (Exception ex)
{
source.SetException(ex);
}
}, sock);
return source.Task;

Related

Read InputStream asynchronously

I'm trying to receive packets and if no bytes are received continue with the code below. await ReadAsync blocks until a new packet is received. Is there any way to just read the current bytes received?
If I don't use await messages aren't received.
byte[] data = new byte[BufferSize];
IInputStream inputStream = socket.InputStream;
IBuffer buffer = data.AsBuffer();
socketInformation.GetStopwatchPingIdleTime().Start();
while (socketInformation.open)
{
try
{
inputStream.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
data = buffer.ToArray();
}
catch (Exception)
{
break;
}
while (true)
{
// Wait for payload size
if (buffer.Length >= 4)
{
int commandType = (int)BitConverter.ToInt16(data, 0);
int payloadSize = (int)BitConverter.ToInt16(data, 2);
int packetSize = PacketHeaderSize + payloadSize;
// Wait for a full message
if (buffer.Length >= packetSize)
{
byte[] packet = new byte[packetSize];
System.Buffer.BlockCopy(data, 0, packet, 0, packetSize);
ParsePacketSequence(socket, socketInformation, packet);
if (buffer.Length > packetSize)
{
int bufferLength = (int)buffer.Length - packetSize;
byte[] newData = new byte[BufferSize];
System.Buffer.BlockCopy(data, packetSize, newData, 0, bufferLength);
data = newData;
buffer.Length = (uint)bufferLength;
}
else if (buffer.Length == packetSize)
{
break;
}
else
{
break;
}
}
else if (buffer.Length == packetSize)
{
break;
}
}
else
{
break;
}
}
if (host)
{
// Send ping to player
if (socketInformation.waitingPong == false &&
socketInformation.GetStopwatchPingIdleTime().ElapsedMilliseconds > 5000)
{
byte[] pingPacket = CreatePacket(6, null);
SendPacket(socket, socketInformation, pingPacket);
socketInformation.waitingPong = true;
}
}
await Task.Delay(33, tokenSource.Token);
}
inputStream.Dispose();
socket.Dispose();
tokenSource.Cancel();
It looks to me you are receiving a stream of messages. When a message is there you want to process it potentially at some later time or at a different place in the code.
A good approach for that is to have one Task continuously reading messages from the socket and putting them into a queue. You can then pull complete messages from the queue whenever you like.
That way you can get rid of most of the logic here. You never have to abort a read request and you never need to check for timeouts.

using tasks and blockingcollections for neverending tasks

I'm looking to read data off the wire and send it to a function to parse the contents. Due to the potential varying size of the message (xml), I could read a whole message, more than one message, or a message portion.
I am trying to implement code using the BlockingCollection where I would call TryAdd when I read the data off the wire and use a consumer thread to pull the data off the BlockingCollection to parse. The examples seem pretty straight-forward, but they only seem to work once, then exit. I want the consumer to continuously parse as messages come in. See code below for what i am currently doing.
handling of messages:
private static BlockingCollection<byte[]> queue = new BlockingCollection<byte[]>();
public XmlHandler()
{
CancellationTokenSource cts = new CancellationTokenSource();
Task.Factory.StartNew(() =>
{
if (Console.ReadKey().KeyChar == 'c')
cts.Cancel();
});
Task.Factory.StartNew(() => ParserWorker(queue, cts.Token));
}
//run producer
public void AddData(byte[] data, int bytesRead)
{
bool success = false;
try
{
success = queue.TryAdd(data);
}
catch (OperationCanceledException)
{
Console.WriteLine("Add loop canceled.");
queue.CompleteAdding();
}
if (success)
{
Console.WriteLine(" Add:{0}", data);
}
else
{
Console.Write(" AddBlocked");
}
System.Console.WriteLine("queue count = " + queue.Count);
}
private static void ParserWorker(BlockingCollection<byte[]> bc, CancellationToken ct)
{
ASCIIEncoding encoder = new ASCIIEncoding();
String xmlString;
while (!bc.IsCompleted)
{
byte[] nextItem;
try
{
if (!bc.TryTake(out nextItem, 0, ct))
{
Console.WriteLine(" Take Blocked");
}
else
{
xmlString = encoder.GetString(nextItem, 0, nextItem.Length);
System.Console.WriteLine(xmlString);
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Taking canceled.");
break;
}
}
}
Reading off the wire (this runs in a thread):
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[8192];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
bytesRead = clientStream.Read(message, 0, 4096);
byte[] temp = new byte[bytesRead];
Array.Copy(message, temp, bytesRead);
/*CODE WILL HANG HERE...*/
ASCIIEncoding encoder = new ASCIIEncoding();
String xmlString = encoder.GetString(message, 0, message.Length);
System.Console.WriteLine(xmlString);
/*DOES NOT GO BEYOND LINE ABOVE */
handler.AddData(message, bytesRead); //xmlhandler
}
catch (Exception e)
{
System.Console.WriteLine(e.ToString());
break;
}
if (bytesRead == 0)
{
break;
}
}
}
So can anyone tell me what I am doing wrong here?

Async socket confusion

My socket client does not appear to be handling situations where the buffer is full and there is more data to be received.
Here is my on receive method, simple version:
private void Recieve()
{
this._clientSocket.BeginReceive(this._buffer, 0, this._buffer.Length, 0,
new AsyncCallback(OnReceive), this._clientSocket);
}
private void OnReceive(IAsyncResult result)
{
Socket clientSocket = (Socket)result.AsyncState;
int bytesRead = clientSocket.EndReceive(result);
this.Recieve();
string data = Encoding.ASCII.GetString(this._buffer, 0, bytesRead);
ThreadPool.QueueUserWorkItem(new WaitCallback(this.HandleDataReceived), data);
}
This doesn't handle situations where there is more data to be received at all. So then I tried this (http://msdn.microsoft.com/en-us/library/bew39x2a.aspx):
private string receivedString = string.Empty;
private void OnReceive(IAsyncResult result)
{
Socket clientSocket = (Socket)result.AsyncState;
int bytesRead = clientSocket.EndReceive(result);
if (bytesRead > 0)
{
receivedString += Encoding.ASCII.GetString(this._buffer, 0, bytesRead);
this.Recieve();
}
else
{
ThreadPool.QueueUserWorkItem(new WaitCallback(this.HandleDataReceived), this.receivedString);
this.receivedString = string.Empty;
}
}
But the problem i'm having with this is that when bytesRead > 0 and I call BeginReceive again I do not get another callback. Did I make some kind of mistake ?
Thanks
In the first code, you have a race condition. Consider:
private void OnReceive(IAsyncResult result)
{
Socket clientSocket = (Socket)result.AsyncState;
int bytesRead = clientSocket.EndReceive(result);
this.Recieve();
string data = Encoding.ASCII.GetString(this._buffer, 0, bytesRead);
ThreadPool.QueueUserWorkItem(new WaitCallback(this.HandleDataReceived), data);
}
You call Receive again, which could overwrite buffer before you send the data to your HandleDataReceived method. I think you want to move that Receive call to after you've converted the data:
private void OnReceive(IAsyncResult result)
{
Socket clientSocket = (Socket)result.AsyncState;
int bytesRead = clientSocket.EndReceive(result);
string data = Encoding.ASCII.GetString(this._buffer, 0, bytesRead);
ThreadPool.QueueUserWorkItem(new WaitCallback(this.HandleDataReceived), data);
this.Recieve();
}
In the second case, you never read 0 bytes because the socket is waiting for data. If there is no data, Socket.Receive will block until there is data available, or until the connection is closed. BeginReceive is waiting for data.

Kill Asynchronous thread

I'm trying to read the HttpResponse.GetResponseStream() Asynchronously
Stream source=myHttpResponse.GetResponseStream();
IAsyncResult asyncResult = source.BeginRead(buffer, 0, new[] { maxDownloadSize, buffer.Length }.Min(), null, null);
But for some reason my application hangs at source.Endread()
int bytesRead = source.EndRead(innerAsyncResult);
I knew that all the Asynchronous calls rely on Threadpool, but is there any way i can kill the thread,
I did placed few controls to kill the asynchronous operation
I have placed an external timer, On Trigger im closing the Stream
System.Threading.Timer timer = new System.Threading.Timer(OnTimerCallback, source, Convert.ToInt32(new TimeSpan(0, 10, 0).TotalMilliseconds), 1);
private static void OnTimerCallback(Object state)
{
Stream stream = state as Stream;
if (stream == null)
return;
try
{
stream.Close();
stream.Dispose();
}
catch (Exception ex)
{
//Log Error
}
throw new Exception("Successfully closed the Stream and killing the Thread");
}
It is closing the stream, but for some reason the application is still hung.
I have placed an external timer, and closing the asyncResult.AsyncWaitHandle.Close()
Both the mechanisms seems to be vain, is there any way i can kill the thread.
Basically what i want to achieve is Read from NetworkStream and Save it in FileStream, and it works fine for a while and later it hung at source.EndRead(innerAsyncResult); Reading from Networkstream is asynchronous , where as saving the data to filestream is synchronous.
Actual Code:
public static void CopyToStreamAsync(this Stream source, Stream destination,
Action<Stream, Stream, Exception> completed, Action<uint> progress,
uint bufferSize)
{
byte[] buffer = new byte[bufferSize];
Action<Exception> done = exception =>
{ //Completed event handler
};
int maxDownloadSize = maximumDownloadSize.HasValue
? (int)maximumDownloadSize.Value
: int.MaxValue;
int bytesDownloaded = 0;
IAsyncResult asyncResult = source.BeginRead(buffer, 0, new[] { maxDownloadSize, buffer.Length }.Min(), null, null);
Action<IAsyncResult, bool> endRead = null;
endRead = (innerAsyncResult, innerIsTimedOut) =>
{
try
{
int bytesRead = source.EndRead(innerAsyncResult);
int bytesToWrite = new[] { maxDownloadSize - bytesDownloaded, buffer.Length, bytesRead }.Min();
destination.Write(buffer, 0, bytesToWrite);
bytesDownloaded += bytesToWrite;
if (!progress.IsNull() && bytesToWrite > 0)
{
//Progress Handlers
}
if (bytesToWrite == bytesRead && bytesToWrite > 0)
{
asyncResult = source.BeginRead(buffer, 0, new[] { maxDownloadSize, buffer.Length }.Min(), null, null);
asyncResult.FromAsync((ia, isTimeout) => endRead(ia, isTimeout), timeout);
}
else
{
done(null);
}
}
catch (Exception exc)
{
done(exc);
}
};
asyncResult.FromAsync((ia, isTimeout) => endRead(ia, isTimeout), timeout);
}

How do I create a task to read data continuously from a TcpClient using tasks and async methods

I am trying to read data continuously from a TcpClient.
Here is my code snippet.
var task = Task.Factory.StartNew(Read);
task.ContinueWith(ant =>
Console.WriteLine("Error: " + ant.Exception.Message),
TaskContinuationOptions.OnlyOnFaulted);
task.ContinueWith(ant =>
{
Log("Log my data");
});
void Read()
{
Task<int> readChunk = Task<int>.Factory.FromAsync(
_stream.BeginRead, _stream.EndRead,
data, bytesRead, data.Length - bytesRead, null,
TaskCreationOptions.AttachedToParent);
readChunk.ContinueWith(rt => { bytesRead = readChunk.Result; }, TaskContinuationOptions.NotOnFaulted
| TaskContinuationOptions.AttachedToParent);
}
This bit of code works fine, but there maybe more data coming on the stream, so I want it to go back and read again.
Starting another Read task doesn't seem right. I am not familiar with the tasks parallel library. Normally I would have written something like this:
while (true)
{
bytesRead += _stream.Read(data, bytesRead, chunkSize);
if(bytesRead == BytesExpected)
break;
}

Categories