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);
}
Related
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.
When cancelling WriteAsync operation on a stream created by HttpWebRequest.GetRequestStream() I get a WebException with the following message:
Cannot close stream until all bytes are written
How can I clean up my HttpWebRequest and my stream when I cancel the operation using CancellationToken?
Here is a PART of my working code(I cleaned some headers for this post so the request may be malformed):
public async Task<bool> UploadAsync(FileInfo fi, CancellationToken ct, IProgress<int> progress = null)
{
FileStream fs = new FileStream(fi.FullName, FileMode.Open);
int bufferSize = 1 * 1024 * 1024;
byte[] buffer = new byte[bufferSize];
int len;
long position = fs.Position;
long totalWrittenBytes = 0;
HttpWebRequest uploadRequest = null;
Stream putStream = null;
try
{
while ((len = fs.Read(buffer, 0, buffer.Length)) > 0)
{
uploadRequest = (HttpWebRequest)WebRequest.Create("http://myuploadUrl");
uploadRequest.Method = "PUT";
uploadRequest.AddRange(position, position + len - 1);
uploadRequest.ContentLength = len;
putStream = uploadRequest.GetRequestStream();
int posi = 0;
int bytesLeft = len;
int chunkSize;
while (bytesLeft > 0)
{
chunkSize = Math.Min(25 * 1024, bytesLeft); //25KB
//HERE IS the WriteAsync part that is being cancelled
await putStream.WriteAsync(buffer, posi, chunkSize, ct);
bytesLeft -= chunkSize;
posi += chunkSize;
totalWrittenBytes += chunkSize;
if (progress != null)
progress.Report((int)(totalWrittenBytes * 100 / fs.Length));
}
putStream.Close();
putStream = null;
position = fs.Position;
var putResponse = (HttpWebResponse)uploadRequest.GetResponse();
putResponse.Close();
uploadRequest = null;
}
//putStream.Write(buffer, 0, len);
}
catch (OperationCanceledException ex)
{
if (putStream != null)
{
putStream.Flush();
putStream.Close();//WebException occur here: Cannot close stream until all bytes are written.
}
return false;
}
finally{
fs.Close();
}
return true;
}
I finally catched the WebException because a post request must write the expected number of bytes (ContentLength property)
Here is my final catch
catch (OperationCanceledException ex)
{
try
{
if (putStream != null)
putStream.Dispose();
}
catch (WebException) { }
return false;
}
finally{
fs.Close();
}
Perhaps I am not sure if I should bother trying to dispose the stream?
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?
I searched the net but couldn't find any way to get progress while downloading file with HttpWebRequest. Does this class support progress at all? Any link, tutorial, hint would be greatly appreciated.
Thanks.
P.S. Here's the code...
private static Task<HttpResponse> MakeAsyncRequest(string requestString)
{
var request = (HttpWebRequest)WebRequest.Create(requestString);
Task<WebResponse> requestTask = Task.Factory.FromAsync(
request.BeginGetResponse,
asyncResult => request.EndGetResponse(asyncResult),
null);
return requestTask.ContinueWith(t => ReadStreamFromResponce(t.Result));
}
private static HttpResponse ReadStreamFromResponce(WebResponse result)
{
var responseobject = new HttpResponse();
var response = (HttpWebResponse)result;
responseobject.StatusCode = (short)response.StatusCode;
if (!IsSuccess(responseobject.StatusCode))
return responseobject;
using (var responseStream = response.GetResponseStream())
using (var ms = new MemoryStream())
{
responseStream.CopyTo(ms);
responseobject.SetResponse(ms.ToArray());
return responseobject;
}
}
Instead of copying response stream in Synchronous, try to copy in Asynchronous and hook the Delegates in the below function, you should be able to get the progress in number of bytes downloaded, display it to progress bar....
public static void CopyToStreamAsync(this Stream source, Stream destination,
Action<Stream, Stream, Exception> completed, Action<uint> progress,
uint bufferSize, uint? maximumDownloadSize, TimeSpan? timeout)
{
byte[] buffer = new byte[bufferSize];
Action<Exception> done = exception =>
{
if (completed != null)
{
completed(source, destination, exception);
}
};
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);
if (innerIsTimedOut)
{
done(new TimeoutException());
}
int bytesToWrite = new[] { maxDownloadSize - bytesDownloaded, buffer.Length, bytesRead }.Min();
destination.Write(buffer, 0, bytesToWrite);
bytesDownloaded += bytesToWrite;
if (!progress.IsNull() && bytesToWrite > 0)
{
progress((uint)bytesDownloaded);
}
if (bytesToWrite == bytesRead && bytesToWrite > 0)
{
asyncResult = source.BeginRead(buffer, 0, new[] { maxDownloadSize, buffer.Length }.Min(), null, null);
// ReSharper disable PossibleNullReferenceException
// ReSharper disable AccessToModifiedClosure
asyncResult.FromAsync((ia, isTimeout) => endRead(ia, isTimeout), timeout);
// ReSharper restore AccessToModifiedClosure
// ReSharper restore PossibleNullReferenceException
}
else
{
done(null);
}
}
catch (Exception exc)
{
done(exc);
}
};
asyncResult.FromAsync((ia, isTimeout) => endRead(ia, isTimeout), timeout);
}
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;