I’m writing C# client for REST service to access Http streaming API. I was supposed to open stream and receive messages that are sent by server in endless stream. On external command reading from stream should stop. HttpClient is used for implementation. Variable streamingActive is used to stop reading from stream since reader.EndOfStream will never return value true.
using (HttpClient httpClient = new HttpClient())
{
httpClient.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
var messagesUri = "http://127.0.0.1:8080/api/messages/progress?sender_id=7837492342";
var stream = httpClient.GetStreamAsync(messagesUri).Result;
using (var reader = new StreamReader(stream))
{
while (streamingActive && !reader.EndOfStream)
{
var currentLine = reader.ReadLine();
try
{
var psMessage = JsonConvert.DeserializeObject<PSMessage>(currentLine);
Console.WriteLine(psMessage);
}
catch (JsonReaderException exc)
{}
}
}
}
It works fine while there are messages coming from the server. If there is no new messages from server for ~300 s call reader.EndOfStream throw exception “The operation has timed out.”
at System.Net.ConnectStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.Read(Byte[] buffer, Int32 offset, Int32 count)
Is there any way to keep stream opened (hypothetically) forever?
If you're willing to let go of the StreamReader, why not open a Stream object directly? The following is for a Universal App (using Windows.Web API)
HttpResponseMessage respMessage;
//note: client and request is defined elsewhere.
respMessage = client.SendRequestAsync(request, HttpCompletionOption.ResponseHeadersRead);
using (var responseStream = await respMessage.Content.ReadAsInputStreamAsync())
{
using (var fileWriteStream = await fileToStore.OpenAsync(FileAccessMode.ReadWrite))
{
while (streamingActive && (await responseStream.ReadAsync(streamReadBuffer, bufferLength, InputStreamOptions.None)).Length > 0)
{
await fileWriteStream.WriteAsync(streamReadBuffer);
}
}
}
This should wait indefinitely for the await responseStream.ReadAsync. If you still need to cancel the opperation you can use a Task with CancellationToken approach.
Related
I have this code that is asynchronously splitting a file into parts, and downloading them using HTTP content range. It then writes the downloaded data to a ViewStream on a Memory Mapped file. I am currently reading from the response stream into a buffer, then writing all the data from the buffer into the ViewStream. Is there a more efficient/faster way to do this? I am not really concerned about memory use, but I am trying to maximize speed. Pieces is a list that contains value tuples indicating the (Start, End) for the piece of the file, and httpPool is a object pool with a bunch of preconfigured HTTP Clients. Any help is greatly appreciated, thank you!
await Parallel.ForEachAsync(pieces,
new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount },
async (piece, cancellationToken) =>
{
//Get a http client from the pool and request for the content range
var client = httpPool.Get();
var request = new HttpRequestMessage { RequestUri = new Uri(url) };
request.Headers.Range = new RangeHeaderValue(piece.Item1, piece.Item2);
//Request headers so we dont cache the file into memory
if (client != null)
{
var message = await client.SendAsync(request,HttpCompletionOption.ResponseHeadersRead,cancellationToken).ConfigureAwait(false);
if (message.IsSuccessStatusCode)
{
//Get the content stream from the message request
using (var streamToRead = await message.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false))
{
//Create a memory mapped stream to the mmf with the piece offset and size equal to the response size
using (var streams = mmf.CreateViewStream(piece.Item1,message.Content.Headers.ContentLength!.Value,MemoryMappedFileAccess.Write))
{
//Copy from the content stream to the mmf stream
var buffer = new byte[bufferSize];
int offset, bytesRead;
// Until we've read everything
do
{
offset = 0;
// Until the buffer is very nearly full or there's nothing left to read
do
{
bytesRead = await streamToRead.ReadAsync(buffer.AsMemory(offset, bufferSize - offset),cancellationToken);
offset += bytesRead;
} while (bytesRead != 0 && offset < bufferSize);
// Empty the buffer
if (offset != 0)
{
await streams.WriteAsync(buffer.AsMemory(0, offset),cancellationToken);
}
} while (bytesRead != 0);
streams.Flush();
streams.Close();
}
streamToRead.Close();
}
}
message.Content.Dispose();
message.Dispose();
}
request.Dispose();
httpPool.Return(client);
});
I don't know how much it is going to help, but I tried to do something. How well does it work?
I also did some refactoring, so here are some notes:
Do not call .Close() or .Dispose() manually if you already have a using block or a using statement. All it does is add noise to your code and confuse anyone reading it. In fact, almost never call .Close() or .Dispose() manually at all.
Do you realize client would never be returned to the pool if any exception occurred in the method? You need to do these things in a finally block or by using an IDisposable struct which returns client to the pool in it's Dispose() implementation. (also, request would not be disposed in the method if any exception occurred, add using)
Whenever you can, prefer if statements that return early rather than ones that wrap the entire rest of the method. The latter is hard to read and maintain.
You are not really benefiting from Parallel as 99% of the method is asynchronously waiting for IO. Just use Task.WhenAll() instead.
I got rid of the custom buffering/copying and just called the CopyToAsync() method on message.Content which accepts a Stream. It should help the performance, probably. I reckon it has to be better optimized than the simplest possible buffer thingee.
Code:
await Task.WhenAll(pieces.Select(p => DownloadToMemoryMappedFile(p)));
// change the piece type from dynamic to what you need
async Task DownloadToMemoryMappedFile(dynamic piece, CancellationToken cancellationToken = default)
{
//Get a http client from the pool and request for the content range
var client = httpPool.Get();
try
{
using var request = new HttpRequestMessage { RequestUri = new Uri(url) };
//Request headers so we dont cache the file into memory
request.Headers.Range = new RangeHeaderValue(piece.Item1, piece.Item2);
if (client is null)
return;
using var message = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
if (!message.IsSuccessStatusCode)
return;
//Create a memory mapped stream to the mmf with the piece offset and size equal to the response size
using var streams = mmf.CreateViewStream(piece.Item1, message.Content.Headers.ContentLength!.Value, MemoryMappedFileAccess.Write);
await message.Content.CopyToAsync(streams).ConfigureAwait(false);
}
finally
{
httpPool.Return(client);
}
}
I implemented a websocket-client using System.Net.WebSockets to communicate with an embedded device. On the device the libwebsocket library is in use.
The main part of the protocol is implemented using JSON strings, which works perfectly but some binary transmission is also needed. Outgoing binary message from Windows are received correctly on the device, but in the case of incoming binary messages a exception is thrown at ReceiveAsync().
I implemented the receive part as a asynchronous loop that calls ReceiveAsync(). In the case of an incoming binary message the WebSocketException with the following error code is thrown:
HResult 0x83760002
E_INVALID_PROTOCOL_FORMAT Protocol data had invalid format.
I don't know what causes it, it throws before i can look at the data i receive. It already worked to receive binary data from the device, but it was an early implementation and only looped back the data that i sent.
private async void ReceiveLoop(CancellationToken cancellationToken)
{
try
{
List<byte> receivedBytes = new List<byte>();
var buffer = new byte[c_bufferSize];
while (!cancellationToken.IsCancellationRequested)
{
var receiveBuffer = new ArraySegment<Byte>(buffer);
WebSocketReceiveResult result;
result = await _webSocket.ReceiveAsync(receiveBuffer, cancellationToken);
receivedBytes.AddRange(receiveBuffer.Array);
// message is complete, return it
if (result.EndOfMessage)
{
if (result.MessageType == WebSocketMessageType.Text)
{
String receivedString = Encoding.UTF8.GetString(receivedBytes.ToArray());
Debug.Print($"{receivedString}");
Debug.Print($"{receivedString.Length.ToString()}");
WebsocketMessage response = new WebsocketMessage(receivedString);
MessagesSubject.OnNext(response);
}
if (result.MessageType == WebSocketMessageType.Binary)
{
WebsocketMessage response = new WebsocketMessage(receivedBytes.ToArray());
MessagesSubject.OnNext(response);
}
receivedBytes.Clear();
buffer = new byte[c_bufferSize];
}
}
}
catch(OperationCanceledException ex)
{
return;
}
catch(Exception ex)
{
MessagesSubject.OnError(ex);
}
}
DataReader.LoadAsync does not detect closed sockets (with InputStreamOptions::Partial)
I am sending data to server via TCP connection and read the response but after and everything is working here. But after 5-6 message my project is hand I didn't find where is error.
Some time i found that connection is closed via host machine
so How can i found that is StreamSocket is connected or not
============Code=============
public async Task<bool> Connect(string host, string port)
{
try
{
socket = new StreamSocket();
HostName hostName = new HostName(host);
CancellationTokenSource _cts = new CancellationTokenSource();
_cts.CancelAfter(5000);
// Connect to the server
await socket.ConnectAsync(hostName, port).AsTask(_cts.Token);
return true;
}
catch (Exception ex)
{
throw ex;
}
}
public async Task<String> SendMessageToServer(string message)
{
try
{
// Create the data writer object backed by the in-memory stream.
using (writer = new DataWriter(socket.OutputStream))
{
writer.WriteString(message);
await writer.StoreAsync();
await writer.FlushAsync();
writer.DetachStream();
return await ReadResponse();
}
}
catch (Exception ex)
{
throw ex;
}
}
private async Task<String> ReadResponse()
{
DataReader reader;
StringBuilder strBuilder = new StringBuilder();
try
{
using (reader = new DataReader(socket.InputStream))
{
uint ReadBufferLength = 1024;
// If task cancellation was requested, comply
//cancellationToken.ThrowIfCancellationRequested();
// Set InputStreamOptions to complete the asynchronous reask(cancellationToken);
reader.InputStreamOptions = Windows.Storage.Streams.InputStreamOptions.Partial;
IAsyncOperation<uint> taskLoad = reader.LoadAsync(ReadBufferLength);
taskLoad.AsTask().Wait(2000);
string msg = string.Empty;
while (reader.UnconsumedBufferLength > 0)
{
strBuilder.Append(reader.ReadString(reader.UnconsumedBufferLength));
msg = strBuilder.ToString();
}
reader.DetachStream();
reader.Dispose();
}
}
catch (Exception ex)
{
return "0";
throw ex;
}
finally
{
// Cleanup once complete
}
return strBuilder.ToString();
}
How can i found that is StreamSocket is connected or not
I tested your code on my side. DataReader/DataWriter are not concerned with "connections" themself as you known. But the connection closed will be detected by method Task.Wait, which is for synchronized waiting on the task completed or throw exceptions. So in your scenario, if the StreamSocked is closed before DataReader.LoadAsync, the taskLoad.AsTask().Wait(2000); will throw the exception:
One or more errors occurred. (An existing connection was forcibly closed by the remote host. )
If the socket connection is closed during DataReader.LoadAsync processed, this time the client cannot detect the closing, it will detect the connection closed next time the client send message to server. In your scenario client will continue sending messages to server which will always catch the connection closing when send new messages. You will got connection closed exception at await writer.StoreAsync(); code line if the server close the connection.
But after 5-6 message my project is hand I didn't find where is error
You may have catch the exception about connection closing. So this may lead by some other reasons. I saw the DataReader in your code snippet only load message one time for length 1024. So if the message send from server is longer than 1024, the client will receive only partial message, and next time when server send new message, the client will still receive the remain message from the last time not the new message. After several times server will back log many data and may lead issues. I recommend you to update the reader.LoadAsync code as follows:
string msg = string.Empty;
var loadsize = await reader.LoadAsync(ReadBufferLength);
while (loadsize >= ReadBufferLength)
{
loadsize = await reader.LoadAsync(ReadBufferLength);
}
if (reader.UnconsumedBufferLength > 0)
{
strBuilder.Append(reader.ReadString(reader.UnconsumedBufferLength));
}
Addtionaly, I strongly recommend you to use StreamReader instead, which will not have the concern as DataReader has, And your ReadResponse() method can just be simply as follows:
private async Task<String> ReadResponse()
{
Stream streamIn = socket.InputStream.AsStreamForRead();
StreamReader reader2 = new StreamReader(streamIn);
string response = await reader2.ReadLineAsync();
return response;
}
More details about StreamSocket in uwp please reference the official sample.
I am trying to use the SerialPort class in .net.
I've opted to keep my service async, so I am using the async-methods on SerialPort.BaseStream.
In my async method, I write a byte[] to the serial port, then start reading until I haven't received any more data in n milliseconds, and return that result.
The problem is, however, that I seem to miss the first byte in all replies other than the very first reply after opening the serial port.
If I close the port after every response (Read), and open it again before doing a new request (Write), the first byte is not missing. This, however, often results in a "Access to the port 'COM4' is denied." exception, if I try to open the port too soon after closing. It also seems very unnecessary to open/close for every write/read.
This is basically what my method looks like:
private async Task<byte[]> SendRequestAsync(byte[] request)
{
// Write the request
await _serialPort.BaseStream.WriteAsync(request, 0, request.Length);
var buffer = new byte[BUFFER_SIZE];
bool receiveComplete = false;
var bytesRead = 0;
// Read from the serial port
do
{
var responseTask = _serialPort.BaseStream.ReadAsync(buffer, bytesRead, BUFFER_SIZE - bytesRead);
if (await Task.WhenAny(responseTask, Task.Delay(300)) == responseTask)
{
bytesRead += responseTask.Result;
}
else
receiveComplete = true;
} while (!receiveComplete);
var response = new byte[bytesRead];
Array.Copy(buffer, 0, response, 0, bytesRead);
return response;
}
Is there anything obviously wrong in the way I am doing this? Is there a smarter way to achieve the same asynchronously?
Just because you're not observing the last ReadAsync() doesn't mean it gets canceled, it's still running, which apparently manifests by it reading the first byte of the following message.
What you should do is to cancel the last ReadAsync() by using a CancellationToken. Note that there is a possible race between the timeout and the read, but I'm assuming that if the timeout elapsed, it's not possible for the read to complete without another write.
The code would look like this:
var cts = new CancellationTokenSource();
do
{
var responseTask = _serialPort.BaseStream.ReadAsync(
buffer, bytesRead, BUFFER_SIZE - bytesRead, cts.Token);
if (await Task.WhenAny(responseTask, Task.Delay(300)) == responseTask)
{
bytesRead += responseTask.Result;
}
else
{
cts.Cancel();
receiveComplete = true;
}
} while (!receiveComplete);
Note that both the cause and the solution are my guesses, it's certainly possible that I'm wrong about one or both of them.
I'm getting a stream from HttpWebResponse.GetResponseStream() where I'm reading data from.
Now I want to implement a Timeout property. The easiest way to do it would be stream.ReadTimeout = timeout but this throws an InvalidOperationException -> Timeouts are not supported on this stream.
Given this, I'm trying to implement the timeout property myself but got stuck on a dispose. This is what I got so far:
public class MyStream : Stream {
private readonly Stream _src;
public override int ReadTimeout { get; set; }
public MyStream (Stream src, int timeout) {
ReadTimeout = timeout;
_src = src;
}
public override int Read(byte[] buffer, int offset, int count) {
var timer = new AutoResetEvent(false);
int read = 0;
ThreadPool.QueueUserWorkItem(
_ => {
read = _src.Read(buffer, offset, count);
timer.Set();
});
bool completed = timer.WaitOne(ReadTimeout);
if (completed) {
return read;
}
throw new TimeoutException(string.Format("waited {0} miliseconds", ReadTimeout));
}
The problem with this code is after is throws a TimeoutException that is being properly handled somewhere. It throws an Exception on _src.Read(buffer, offset, count) saying that the _src stream was disposed.
Is there a way to cancel the ThreadPool method or should I use a better approach and which one?
Thanks
EDIT
As asked by #JotaBe, were's the code where I get the stream from HttpWebResponse:
_httpRequest = WebRequest.CreateHttp(url);
_httpRequest.AllowReadStreamBuffering = false;
_httpRequest.BeginGetResponse(
result =>
{
try {
_httpResponse = (HttpWebResponse)_httpRequest.EndGetResponse(result);
stream = _httpResponse.GetResponseStream();
}
catch (WebException) {
downloadCompleted.Set();
Abort();
}
finally {
downloadCompleted.Set();
}
},
null);
bool completed = downloadCompleted.WaitOne(15 * 1000);
if (completed) {
return new MyStream(stream, 10000);
}
If you're trying to get a timeout if you don't receive and answer form a web server, you're trying to do it in the wrong place.
To get a Response, you usually make a Request:
HttpWebResponse response = (HttpWebResponse)request.GetResponse ();
This is the operation which can timeout. You have to call it in a differente way, using the Begin/End asynchronous pattern.
There is a full example of this in
MSDN doc for HttpWebRequest.BeginGetResponse Method
This example uses a callback function. However, there are many different ways to use Begin/End. For example, you can use a WaitHandle available in IAsyncResult like this:
IAsyncResult ar = req.BeginGetResponse(yourCallback, null);
bool completed = ar.AsyncWaitHandle.WaitOne(15000 /*your timeout in miliseconds*/);
This will wait 15 seconds. If the response arrives before this, completed will be true. If not, completed will be false. You can then use the HttpWebRequest.Abort() method to abort the request.
The Begin/End pattern takes charge of managing the neccesary threads.
I ended up using Nomad101 suggestion and surround the read with a try/catch.