C# WebClient freezes after a certain time in a while(true) loop - c#

I'm working on my Grade Project and run into this issue.
I'm making a control panel for a parking lot and I use Arduino for reading the sensors and WebServer that sends the values of the sensors in a JSON file to a connected client.
I've made a C# form that indicates if there is a car parked on spot 1,2,... etc.
So, I designed a little form that does this, it contains a second threat that runs an infinite loop which creates a WebClient that will collect the JSON from the Arduino WebServer and when done the JSON will be handled and the spots are being indicated.
After a few seconds, the thread freezes, more specific the WebClient that is opening the read just freezes.
I've spent a few days now to find out how to fix this issue, no success yet.
So I tried to make another thread that checks if the connection started and it takes more than a second to get the data it will try to abort the thread of the WebClient and creating a new one, to resume getting the JSON I thought.
But when the Timer hits a second the thread that tries to abort the other threat also freezes.
I gave up and decided to ask it on StackOverflow because I can't find a solution.
this is my thread for the WebClient:
private void startConnectionThread()
{
new Thread(() =>
{
while (true)
{
try
{
WebClient client = new WebClient();
Stream data = client.OpenRead("http://192.168.0.177/");
StreamReader reader = new StreamReader(data);
strResult = reader.ReadToEnd();
data.Close();
reader.Close();
handleResult(strResult);
Thread.Sleep(1000);
}
catch (Exception ex)
{
}
}
}).Start();
}
I can use all your help. Thanks in advance.
EDIT:
This is my method with HttpClient the same issue but after a few seconds the freezing connection refreshes and it works again, I see this as a solution to my problem. And when I add client.Timeout = TimeSpan.FromMilliseconds(300); it does abridge the time out.
private void startConnectionThread()
{
new Thread(async() =>
{
HttpClient client = new HttpClient();
client.Timeout = TimeSpan.FromMilliseconds(200);
while (true)
{
try
{
HttpResponseMessage response = await client.GetAsync("http://192.168.0.177/");
strResult = await response.Content.ReadAsStringAsync();
handleResult(strResult);
}
catch (Exception ex)
{
}
}
}).Start();
}

You need to use the same WebClient instance, not recreate it in the loop as the other previously created clients are still in use, taking up connections and listening.
So move it out of your loop...
var client = new WebClient();
while (true)
{
try
{
using (var data = client.OpenRead("http://192.168.0.177/"))
{
using (var reader = new StreamReader(data))
{
strResult = reader.ReadToEnd();
handleResult(strResult);
Thread.Sleep(1000);
}
}
}
catch (Exception ex)
{
}
}

This seems to be the solution for me, using HttpClient and the change in the timeout property did make the change.
private void startConnectionThread()
{
new Thread(async() =>
{
HttpClient client = new HttpClient();
client.Timeout = TimeSpan.FromMilliseconds(200);
while (true)
{
try
{
HttpResponseMessage response = await client.GetAsync("http://192.168.0.177/");
strResult = await response.Content.ReadAsStringAsync();
handleResult(strResult);
}
catch (Exception ex)
{
}
}
}).Start();
}

Related

Does these HttpClient call be called in parallel?

I've a .NET Core web app that, once a web method (i.e. Test()) is invoked, call another remote api.
Basically, I do it this way (here's a POST example, called within a web method Test()):
public T PostRead<T>(string baseAddress, string url, out bool succeded, object entity = null)
{
T returnValue = default(T);
succeded = false;
try
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", MyApiKey);
HttpResponseMessage res = null;
string json = JsonConvert.SerializeObject(entity);
var body = new StringContent(json, UnicodeEncoding.UTF8, "application/json");
var task = Task.Run(() => client.PostAsync($"{baseAddress}/{url}", body));
task.Wait();
res = task.Result;
succeded = res.IsSuccessStatusCode;
if (succeded)
{
returnValue = JsonConvert.DeserializeObject<T>(res.Content.ReadAsStringAsync().Result);
}
else
{
Log($"PostRead failed, error: {JsonConvert.SerializeObject(res)}");
}
}
}
catch (Exception ex)
{
Log($"PostRead error: {baseAddress}/{url} entity: {JsonConvert.SerializeObject(entity)}");
}
return returnValue;
}
Question is: if Test() in my Web App is called 10 times, in parallel, do the POST requests to the remote server are be called in parallel or in serial?
Because if they will be called in serial, the last one will take the time of the previous ones, and that's not what I want.
In fact, when I have huge list of requests, I often receive the message A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
Question is: if Test() in my Web App is called 10 times, in parallel, do the POST requests to the remote server are be called in parallel or in serial?
They will be called in parallel. Nothing in the code would make it in a blocking way.
In fact, when I have huge list of requests, I often receive the message A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
There are at least 2 things that need to be fixed here:
Properly using async/await
Properly using HttpClient
The first one is easy:
public async Task<T> PostRead<T>(string baseAddress, string url, out bool succeded, object entity = null)
{
succeded = false;
try
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", MyApiKey);
string json = JsonConvert.SerializeObject(entity);
var body = new StringContent(json, UnicodeEncoding.UTF8, "application/json");
var responseMessage = await client.PostAsync($"{baseAddress}/{url}", body);
var responseContent = await responseMessage.Content.ReadAsStringAsync();
if (responseMessage.IsSuccessStatusCode)
{
succeeded = true;
return JsonConvert.DeserializeObject<T>();
}
else
{
// log...
}
}
}
catch (Exception ex)
{
// log...
}
return default; // or default(T) depending on the c# version
}
The second one is a bit more tricky and can quite possibly be the cause of the problems you're seeing. Generally, unless you have some very specific scenario, new HttpClient() is plain wrong. It wastes resources, hides dependencies and prevents mocking. The correct way to do this is by using IHttpClientFactory, which can be very easily abstracted by using the AddHttpClient extension methods when registering the service.
Basically, this would look like this:
services.AddHttpClient<YourClassName>(x =>
{
x.BaseAddress = new Uri(baseAddress);
x.DefaultRequestHeaders.Add("Authorization", MyApiKey);
}
Then it's a matter of getting rid of all using HttpClient in the class and just:
private readonly HttpClient _client;
public MyClassName(HttpClient client) { _client = client; }
public async Task<T> PostRead<T>(string url, out bool succeded, object entity = null)
{
succeded = false;
try
{
string json = JsonConvert.SerializeObject(entity);
var responseMessage = await _client.PostAsync($"{baseAddress}/{url}", body);
//...
}
catch (Exception ex)
{
// log...
}
return default; // or default(T) depending on the c# version
}
[HttpClient call using parallel and solve JSON Parse error]
You can use task. when for parallel call . but when you use N number of calls and try to parse as JSON this may throw an error because result concatenation change the Json format so we can use Jarray. merge for combining the result .
Code
public async Task < string > (string baseAddress, string url, out bool succeded, object entity = null) {
using(HttpClient client = new HttpClient()) {
string responseContent = string.Empty;
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Authorization", MyApiKey);
string json = JsonConvert.SerializeObject(entity);
var body = new StringContent(json, UnicodeEncoding.UTF8, "application/json");
var tasks = new List < Task < string >> ();
var jArrayResponse = new JArray();
int count = 10; // number of call required
for (int i = 0; i <= count; i++) {
async Task < string > RemoteCall() {
var response = await client.PostAsync($"{baseAddress}/{url}", body);
return await response.Content.ReadAsStringAsync();
}
tasks.Add(RemoteCall());
}
await Task.WhenAll(tasks);
foreach(var task in tasks) {
string postResponse = await task;
if (postResponse != "[]") {
var userJArray = JArray.Parse(postResponse);
jArrayResponse.Merge(userJArray);
}
}
responseContent = jArrayResponse.ToString();
return responseContent;
}
}

Web API MultipartAsync Task no result

The goal is to upload a single file to my webserver and then store it to mssql database by using multipartcontent. While the file is uploading, a progress bar should be displayed in the client (WPF application). The following code sample shows only the upload to memorystream (no database connection).
The connection from client to server works, the upload to the MemoryStream at server-side works and receiving receiving the percentage to client side works (section ContinueWith in my sample code). The problem is, the client doesn't receive the final CreateResponse request - like a timeout or lost connection, I am not sure because i doesn't get an error/exception. The client never receives the task final result.
WebApi:
public class AttachmentsController : ApiController
{
[HttpPost]
[Route("api/Attachments/Upload")]
public async Task<HttpResponseMessage> Upload()
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
try
{
AttachmentUpload _resultAttachmentUpload = null;
var _provider = new InMemoryMultipartFormDataStreamProvider();
await Request.Content.ReadAsMultipartAsync(_provider)
.ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
throw new HttpResponseException(
HttpStatusCode.InternalServerError);
}
return new AttachmentUpload()
{
FileName = _provider.Files[0].Headers.ContentDisposition
.FileName.Trim('\"'),
Size = _provider.Files[0].ReadAsStringAsync().Result
.Length / 1024
};
});
return Request.CreateResponse<AttachmentUpload>(HttpStatusCode.Accepted,
_resultAttachmentUpload);
}
catch (Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
e.ToString());
}
}
}
WPF Client UploadService.cs:
private async Task<Attachment> UploadAttachment(AttachmentUpload uploadData,
string filePath)
{
try
{
var _encoding = Encoding.UTF8;
MultipartFormDataContent _multipartFormDataContent =
new MultipartFormDataContent();
_multipartFormDataContent.Add(new StreamContent(new MemoryStream(
File.ReadAllBytes(filePath))), uploadData.FileName, uploadData.FileName);
_multipartFormDataContent.Add(new StringContent(uploadData.Id.ToString()),
"AttachmentId", "AttachmentId");
_multipartFormDataContent.Add(new StringContent(
uploadData.Upload.ToString(CultureInfo.CurrentCulture)), "AttachmentUpload",
"AttachmentUpload");
_multipartFormDataContent.Add(new StringContent(
uploadData.DocumentId.ToString()), "DocumentId", "DocumentId");
_multipartFormDataContent.Add(new StringContent(
uploadData.User, _encoding), "User", "User");
//ProgressMessageHandler is instantiate in ctor to show progressbar
var _client = new HttpClient(ProgressMessageHandler);
_client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("multipart/form-data"));
_client.Timeout = TimeSpan.FromMinutes(5);
var _requestUri = new Uri(BaseAddress + "api/Attachments/Upload");
var _httpRequestTask = await _client.PostAsync(_requestUri,
_multipartFormDataContent)
.ContinueWith<AttachmentUpload>(request =>
{
var _httpResponse = request.Result;
if (!_httpResponse.IsSuccessStatusCode)
{
throw new Exception();
}
var _response = _httpResponse.Content.ReadAsStringAsync();
AttachmentUpload _upload =
JsonConvert.DeserializeObject<AttachmentUpload>(_response.Result);
return _upload;
});
var _resultAttachment = _httpRequestTask;
return new Attachment()
{
Id = _resultAttachment.Id,
FileName = _resultAttachment.FileName,
Comment = _resultAttachment.Comment,
Upload = _resultAttachment.Upload,
};
}
catch (Exception e)
{
//Handle exceptions
//file not found, access denied, no internet connection etc etc
var tmp = e;
throw e;
}
}
The program lacks at var _httpRequestTask = await _client.PostAsync(...).
The debugger never reaches the line var _resultAttachment = _httpRequestTask;.
Thank you very much for your help.
First of all, don't mix await and ContinueWith, the introduction of async / await effectively renders ContinueWith obsolete.
The reason that var _resultAttachment = _httpRequestTask; is never hit is because you have created a deadlock.
WPF has a synchronisation context, that ensures continuations resume on the UI thread.
In the line AttachmentUpload _upload = JsonConvert.DeserializeObject<AttachmentUpload>(_response.Result);, _response.Result is a blocking call; it blocks the current thread, until the Task referenced by _response has completed.
The ReadAsStringAsync method, which generated the Task, will attempt to resume once the asynchronous work has completed, and the WPF synchronisation context will force it to use the UI thread, which has been blocked by _response.Result, hence the deadlock.
To remedy this, use the await keyword for every asynchronous call:
var _httpResponse = await _client.PostAsync(_requestUri, _multipartFormDataContent);
if (!_httpResponse.IsSuccessStatusCode)
{
throw new Exception();
}
var _response = await _httpResponse.Content.ReadAsStringAsync();
var _resultAttachment = JsonConvert.DeserializeObject<AttachmentUpload>(_response);
You should also notice that the code is much more readable without the ContinueWith.

NetworkStream Async Read -> Cancel

Currently I try to read and write Async to/from a network stream. My software is the Client part and the server can send informations on its own or respond to commands I send him.
So I need a socket which
reads all the time (in case the server sends status informations)
stops reading when I want to send commands (commands can be sequences of data with multible Write and Read operations)
So I thought it would be a good approach to create a Semaphore and a Background Task which handles the server sent messages and in case I want to send a command I block the semaphore and have full access to read/write operations to the socket.
Here is what I do currently.
private TcpClient _tcpClient = new TcpClient();
protected SemaphoreSlim ClientSemaphore { get; } = new SemaphoreSlim(1, 1);
public async Task ConnectAsync()
{
if (_tcpClient.Connected)
{
await DisconnectAsync();
}
await _tcpClient.ConnectAsync(Hostname, RemotePort);
//here the background Task is started
_ = AutoReceiveMessages();
}
private async Task AutoReceiveMessages()
{
while (_tcpClient.Connected)
{
//enter and lock semaphore
await ClientSemaphore.WaitAsync();
try
{
//read from socket until timeout (ms)
var msg = await ReadFromSocket(2000);
foreach (var cmd in SplitMessageInTelegrams(msg))
{
Console.WriteLine("MESSAGE --> " + cmd);
}
}
catch (Exception ex)
{
}
finally
{
//release semaphore
ClientSemaphore.Release();
}
}
}
private async Task<string> ReadFromSocket(double timeout = 0)
{
var buf = new byte[4096];
var stream = _tcpClient.GetStream();
//read from stream or timeout
var amountReadTask = stream.ReadAsync(buf, 0, buf.Length);
var timeoutTask = Task.Delay(TimeSpan.FromMilliseconds(timeout));
await Task.WhenAny(timeoutTask, amountReadTask)
.ConfigureAwait(false);
//timeout
if (!amountReadTask.IsCompleted)
{
throw new TimeoutException("Timeout");
}
//no timeout
return Encoding.ASCII.GetString(buf, 0, amountReadTask.Result);
}
But this do not work as I expected...
I use this methode to send a message to the server and in WireShark I see the server resonds with the same message
protected async Task SendTelegramAsync(ITelegram telegram)
{
await ClientSemaphore.WaitAsync();
try
{
_ = telegram ?? throw new ArgumentException($"{nameof(telegram)}");
if (!_tcpClient.Connected) throw new InvalidOperationException("Socket not connected!");
var buf = new byte[4096];
var stream = _tcpClient.GetStream();
var msg = Encoding.ASCII.GetBytes("\x02" + telegram.GetCommandMessage() + "\x03");
Console.WriteLine("WRITE --> " + msg);
await stream.WriteAsync(msg, 0, msg.Length);
//comment AutoReceiveMessage and remove comment from this
//and I get responses from the server
//var test = await ReadFromSocket(2000);
}
finally
{
ClientSemaphore.Release();
}
}
I know in this case I do not need the semaphore but later I want to create sequences so one command consists of multible writes and reads and as long as the command is executed I do not want to use the AutoReceiveMessages method.
The problem now is
If I use it like this I never get a response the ReadFromSocket method always get the timeout even when wireshark tell me the server has responded
But even better if I disable AutoReceiveMessages (just comment _ = AutoReceiveMessages()) and use ReadFromSocket directly in SendTelegramAsync() everything work as expected.
So I think the problem is something related to the background task and the ReadAsync but I couldnt figure it out...
Got It!
stream.DataAvailable is your friend (or my friend :)).
If I check before the ReadAsync if DataIsAvailable then I have no problem anymore.
if (_tcpClient.GetStream().DataAvailable)
var msg = await ReadFromSocket(DEFAULT_TIMEOUT);

ASP.NET web API - System.Threading.Tasks.TaskCanceledException: A task was canceled dilemma

I have an asp.net web API. (net framework 4.6.1) I am calling a 3rd party rest API (Product) in one of my actions. In functional tests, everything works as expected, no problem. But when I do spike tests (let's say, 100 users, 10 seconds ramp-up time) I am getting System.Threading.Tasks.TaskCanceledException: A task was canceled error after some successful responses. I googled and came across that it might be due to timeout. I added TimeSpan.FromSeconds(600); but still getting the same error.
Here is how I call this 3rd party API. Is there any problem with an async call? Would you please have a look at my code?
public class UtilitiesTest
{
private static readonly HttpClient _httpClient = new HttpClient();
//some not relevant code here
public static async Task<HttpResponseMessage> CallRazer(GameRequest gameRequest, string url)
{
try
{
FormUrlEncodedContent content = null;
if (url == "Product/")
{
try
{
//some code here
//Timeout
_httpClient.Timeout = TimeSpan.FromSeconds(600);
//Call Game
var response = await _httpClient.PostAsync("https://test.com/" + url, content);
return response;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
finally
{
content.Dispose();
}
}
else
{
try
{
//some code here
//Call Game
var response = await _httpClient.PostAsync("https://test.com/" + url, content);
return response;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
finally
{
content.Dispose();
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
and here is how I call Razer:
private async Task<HttpResponseMessage> CallProducts()
{
//some code here
#region Call Razer for products
//**Call Razer**
var response = await Utilities.CallRazer(products, "Product/");
var htmlResponse = await response.Content.ReadAsStringAsync();
var model = JsonConvert.DeserializeObject<ProductResponseDto>(htmlResponse);
// some code here
return response;
}
Somebody in another forum, suggested me to use HTTPWeb Request instead of Httpclient. So I changed my code like below and all my troubles are gone.
//HTTPWebRequest
var request = (HttpWebRequest) WebRequest.Create("http://test.com" + url);
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
var keyValueContent = productRequest.ToKeyValue();
var formUrlEncodedContent = new FormUrlEncodedContent(keyValueContent);
var urlEncodedString = await formUrlEncodedContent.ReadAsStringAsync();
using (var streamWriter = new StreamWriter(await request.GetRequestStreamAsync()))
{
streamWriter.Write(urlEncodedString);
}
HttpWebResponse httpResponse = (HttpWebResponse) (await request.GetResponseAsync());
response = new HttpResponseMessage
{
StatusCode = httpResponse.StatusCode,
Content = new StreamContent(httpResponse.GetResponseStream()),
};
return response;
If it's still timing out after 600 seconds, then your web service just can't keep up.
But yes, a timeout will generate a TaskCanceledException, which is not intuitive at all. This will actually be changing in .NET Core at least (just fixed last week), but that doesn't help you.
Here is code I've used to rethrow a TimeoutException instead. The only other reason a TaskCanceledException could be thrown is if you passed a CancellationToken that ended up being cancelled, but you're not. So it's definitely a timeout.
} catch (TaskCanceledException) {
//Could have been caused by cancellation or timeout if you used one.
//If that was the case, rethrow.
//cancellationToken.ThrowIfCancellationRequested();
//HttpClient throws TaskCanceledException when the request times out. That's dumb.
//Throw TimeoutException instead and say how long we waited.
string time;
if (_httpClient.Timeout.TotalHours > 1) {
time = $"{_httpClient.Timeout.TotalHours:N1} hours";
} else if (_httpClient.Timeout.TotalMinutes > 1) {
time = $"{_httpClient.Timeout.TotalMinutes:N1} minutes";
} else if (_httpClient.Timeout.TotalSeconds > 1) {
time = $"{_httpClient.Timeout.TotalSeconds:N1} seconds";
} else {
time = $"{_httpClient.Timeout.TotalMilliseconds:N0} milliseconds";
}
throw new TimeoutException($"No response after waiting {time}.");
}

C# encapsulation when getting updates from asynchronous method

I have a tcp connection I want to keep open in the HandleConnectionAsync method of the server class. It will be receiving continuous updates from a client.
private async void HandleConnectionAsync(TcpClient tcpClient)
{
Console.WriteLine("Got connection request from {0}", tcpClient.Client.RemoteEndPoint.ToString());
try
{
using (var networkStream = tcpClient.GetStream())
using (var reader = new StreamReader(networkStream))
using (var writer = new StreamWriter(networkStream))
{
writer.AutoFlush = true;
while (true)
{
string dataFromClient = await reader.ReadLineAsync();
Console.WriteLine(dataFromClient);
await writer.WriteLineAsync("FromServer-" + dataFromClient);
}
}
}
catch (Exception exp)
{
Console.WriteLine(exp.Message);
}
}
I want to be able to receive the updates the reader puts into dataFromClient without having to put my code in the midst of my server implementation class.
So:
static void Main(string[] args)
{
Server s = new Server();
s.start();
//get dataFromClient as it comes in here
}
Problem is I'm not sure how to do this. Some kind of callback or event?
There are many many ways.
The least code is to pass an Action to your server eg:
Server s = new Server();
s.start(dataFromClient =>
{
// do something with data from client
});
And in HandleConnectionAsync call the action eg:
private async void HandleConnectionAsync(TcpClient tcpClient, Action<string> callback)
{
..
// Console.WriteLine(dataFromClient);
callback(dataFromClient);
..
However this does not address a few things e.g. many clients at the same time and needing to know which client is which.

Categories