await for a response matching a request sequence number - c#

I am trying to implement a network applicative protocol client library, composed of binary request/responses over TCP connection, and I would like this client to be fully asynchronous relying on async/await constructs of c#5.
Each request sent on the NetworkStream contains an associated applicative sequence number. The reponse made to the request, must specify the same sequence number so that response can be matched with original request.
When I issue a request R1, response to R1 can of course come at any time in the future, and other responses for other requests may come on the line before the actual response to R1.
What I would like to do in the client code of the library is something as simple stupid as
var resp = await SendSomeRequestAsync(req);
SendSomeRequestAsync will send the request asynchronously on the line (got that part right) and somehow await the associated response (matching the sequence number that was sent in the request), such as (in SendSomeRequestAsync)
var dummy = await _ns.WriteAsync(rawBytes, 0, rawBytes.Length); // _ns is a NetworkStream
var resp = await GetResponseMatchingSequenceNumberAsync(sequenceNumber);
I have a loop that is launched when client start connection which is reading incoming responses on the connection asynchronously :
while (true)
{
Response rsp = await ReadNextResponseAsync(_ns);
DispatchReceivedResponse(rsp);
}
I can't figure out how to implement the GetResponseMatchingSequenceNumberAsync, or if I am already doing it totally wrong.
Hope my question is clear enough.
Thanks

I've run into exactly this issue, and the following seemed pretty clean:
Create an IDictionary<int,Response> and store TaskCompletionSource<Response> instances in it. When you get a response back, find the TaskCompletionSource and set it completed. I make no claims for the thread safety of this code. The dictionary should probably be of the concurrent kind, or at least accessed in a lock of some sort.
public Task<Response> GetResponseMatchingSequenceNumberAsync(sequenceNumber)
{
var tcs=new TaskCompletionSource<Response>();
pendingTasksDictionary.Add(sequenceNumber,tcs);
return tcs.Task;
}
private void ResponseHandler(int sequenceNumber,Response response)
{
var pendingTcs=pendingTasksDictionary[sequenceNumber];
//remove from dictionary
pendingTcs.SetCompleted(response);
}

Related

Is it possible for HTTP server to receive requests out of order even if they are sent sequentially?

(This discussion might not be specific to C#...)
I have a C# method SendMultipleRequests that sends HTTP POST request 10 times sequentially.
Is it possible for the server to receive requests out of order?
If my understanding is correct if the requests are sent concurrently (without await), the server could receive requests out of order, but in the example below it needs to wait for the response to be received at the client before sending next request, so the server will receive requests in order.
public async Task SendRequest(int i)
{
// definition of endpoint is omitted in this example
var content = new StringContent($"I am {i}-th request");
await HttpClient.PostAsync(endpoint, content);
}
public async Task SendMultipleRequests()
{
for (int i = 0; i < 10; i++)
{
await SendRequest(i);
}
}
with await your app will wait for the task returned by PostAsync to finish before it issues the next request - see the docs for postasync where it says “This operation will not block. The returned Task<TResult> object will complete after the whole response (including content) is read.” - using await will mean that you will only issue the next request after you I’ve read the content of the previous response
If you remove the await then your code will queue up ten tasks and start working on them all in some undefined order. The server will see requests in an unspecified order. This may be further exacerbated by the fact that the requests may take different routes through the internet, some slower. If you remove the await then you should capture the returned task into a list, then you can use something like await Task.WhenAll(list) to wait for them all to complete (unless you really want to fire and forget in which case you can assign them to the discard _ = client.PostAsync... but keeping the task allows you to discover and deal with exceptions that arose)

Process incoming FileStream asynchronously

I'm reading a file from user upload and it was working synchronously. I needed to change it in order to immediately send a "received" alert to the user, then read the file asynchronously while the user would periodically poll back to see if the read was finished.
Here is what my code looks like right now:
public FileUpload SaveFile(Stream stream)
{
FileUpload uploadObj = //instantiate the return obj
var task = Task.Run(async () => await ProcessFileAsync(stream));
return upload;
}
public async Task ProcessFileAsync(Stream stream)
{
StreamReader file = new StreamReader(stream);
CsvReader csv = new CsvReader(file, CultureInfo.InvariantCulture);
while (await csv.ReadAsync())
{
//read the file
}
}
the issue I'm having is that by the time I call the csv.ReadAsync() method, the Stream object has been disposed. How do I access the Stream when I want the SaveFile() method to return a value to the user, but the act of returning disposes the Stream object?
The point here is that you're working within the constraints of ASP.NET, which abstracts away a lot of the underlying HTTP stuff.
When you say you want to process a user-uploaded file asynchronously, you want to step out of the normal order of doing things with HTTP and ASP.NET. You see, when a client sends a request with a body (the file), the server receives the request headers and kicks off ASP.NET to tell your application code that there's a new request incoming.
It hasn't even (fully) read the request body at this point. This is why you get a Stream to deal with the request, and not a string or a filename - the data doesn't have to be arrived at the server yet! Just the request headers, informing the web server about the request.
If you return a response at that point, for all HTTP and ASP.NET care, you're done with the request, and you cannot continue reading its body.
Now what you want to do, is to read the request body (the file), and process that after sending a response to the client. You can do that, but then you'll still have to read the request body - because if you return something from your action method before reading the request, the framework will think you're done with it and dispose the request stream. That's what's causing your exception.
If you'd use a string, or model binding, or anything that involves the framework reading the request body, then yes, your code will only execute once the body has been read.
The short-term solution that would appear to get you going, is to read the request stream into a stream that you own, not the framework:
var myStream = new MemoryStream();
await stream.CopyTo(myStream);
Task.Run(async () => await ProcessFileAsync(myStream));
Now you'll have read the entire request body and saved it in memory, so ASP.NET can safely dispose the request stream and send a response to the client.
But don't do this. Starting fire-and-forget tasks from a controller is a bad idea. Keeping uploaded files in memory is a bad idea.
What you actually should do, if you still want to do this out-of-band:
Save the incoming file as an actual, temporary file on your server
Send a response to the client with an identifier (the temporarily generated filename, for example a GUID)
Expose an endpoint that clients can use to request the status using said GUID
Have a background process continuously scan the directory for newly uploaded files and process them
For the latter you could hosted services or third-party tools like Hangfire.
You'll need to either do this if the environment warrants:
var result = task.Result;
//do stuff
...or
public Task<FileUpload> SaveFile(Stream stream)
{
var uploadObj = //instantiate the return obj
await ProcessFileAsync(stream);
return uploadObj;
}
See here for a thorough discussion on fire-and-forget if you go that route:
Web Api - Fire and Forget

Sending multiple requests in any order but no more that 1 request/second

In my c# wpf application when a user presses a button I need to send to a server 10-20 requests. They can be sent in an arbitrary order but there has to be at least 10 of them because the server returns the results paginated.
Each client (my c# is a client) has an apy key and server can only handle 1 request per second per a certain client, otherwise the server returns an error.
How can send those requests properly? Should I necessarily use async and await? And can I send them in parallel and how? Doesn't async in this case means that they'll be sent in parallel?
And, how can I ensure that only 1 request per second is sent? I gathered it's not good to mix the threads, which is Thread.Sleep(1000) for my case, and async/await.
So, you could create a bunch of tasks that stagger the job by a second each time.
Something like:
List<Uri> uris=new List<Uri>(); //fill with uris
var tasks = uris.Select(async (u, i)=>{
await Task.Delay(TimeSpan.FromSeconds(i));
using(var wc = new WebClient())
{
return await wc.DownloadStringTaskAsync(u);
}
});
var results = await Task.WhenAll(tasks);

How do I remove the delay between HTTP Requests when using Asynchronous actions in ASP.NET?

I am using HttpClient to send a GET request to a server inside of a while loop
while (cycle < maxcycle)
{
var searchParameters = new ASearchParameters
{
Page = cycle++,
id = getid
};
var searchResponse = await Client.SearchAsync(searchParameters);
}
and the SearchAsync contains
public async Task<AuctionResponse> SearchAsync()
{
var uriString = "Contains a https url with parameters"
var searchResponseMessage = await HttpClient.GetAsync(uriString);
return await Deserialize<AuctionResponse>(searchResponseMessage);
}
The thing is after every request there is a delay before the next request is started.
you can see this in fiddler timeline and also in fiddler there is "Tunnel To" example.com:443 before every request
Question : Why is there a delay and how to remove it ?
I see two things that are happening here. First, depending on the deserializer, it may take a while to translate your response back into an object. You might want to time that step and see if that's not the majority of your time spent. Second, the SSL handshake (the origin of your "tunnel to") does require a round trip to establish the SSL channel. I thought HttpClient sent a Keep-Alive header by default, but you may want to see if it is A) not being sent or B) being rejected. If you are re-establishing an SSL channel for each request, that could easily take on the order of a hundred ms all by itself (depending upon the server/network load).
If you're using Fiddler, you can enable the ability to inspect SSL traffic to see what the actual request/response headers are.
I believe you see this delay for a couple of reasons. Based on the code you provided, all other actions besides the request itself take up some fraction of the time between requests. So deserializing the response will add to a delay.
Also, the delay might be tied to the amount of data that is being returned and processed further down the stack. I tried to recreate the scenario you describe in your question with the following code:
const int MaxNumberOfCycles = 10;
static void Main()
{
Start().Wait();
}
async Task Start()
{
var client = new Client();
var cycle = 0;
while (cycle < MaxNumberOfCycles)
{
var response = await client.SearchAsync(cycle++);
}
}
class Client
{
public async Task<HttpResponseMessage> SearchAsync(int n)
{
// parameter 'n' used to vary web service response data
var url = ... // url removed for privacy
using (var client = new HttpClient())
using (var response = await client.GetAsync(url))
{
return response;
}
}
}
With small response sizes I saw no delay between requests. As response sizes increased I began to see slightly longer delays. Here's a screenshot for a series of requests returning 1MB responses:
One thing I noticed about your scenario is that your transfer activity graph shows a solid black line at the end of each request. This line indicates the "time to first byte", meaning that response processing did not even start until the very end of your request.
Another issue you might consider is that Fiddler is that causing these delays. I noticed that your responses aren't being streamed by Fiddler, which probably impacts the results. You can read more about response streaming in Fiddler.
I hope some of this information helps...

How can I POST an HTTP request and wait for a callback without blocking the current thread?

We have a .NET application that uses an HTTP based API where we POST a request to a third party HTTP endpoint (that is not under our control) and it calls us back at a later time on an HTTP endpoint that we give it; something like:
WebRequest request = WebRequest.Create(urlToMethod);
request.Method = #"POST";
request.Headers.Add(#"Callback", "http://ourserver?id="+id );
We make thousands upon thousands of these calls and so we'd like to be as effecient as possible (in terms of speed/memory/threads etc.)
As far as the callback code is concerned, we have a type that acts as a listener; this is how we start it up:
_httpListener = new HttpListener();
_httpListener.Prefixes.Add(ourServer);
_httpListener.Start();
_httpListener.BeginGetContext(callback, null);
When the server calls us back, it hits our callback method which looks something like this:
HttpListenerContext context = _httpListener.EndGetContext(result);
HttpListenerResponse httpListenerResponse = context.Response;
httpListenerResponse.StatusCode = 200;
httpListenerResponse.ContentLength64 = _acknowledgementBytes.Length;
var output = httpListenerResponse.OutputStream;
output.Write(_acknowledgementBytes, 0, _acknowledgementBytes.Length);
context.Response.Close();
var handler = ResponseReceived;
if (handler != null)
{
handler(this, someData);
}
So we have a single instance of this listener (_which internally uses HttpListener) and for every response it gets, it informs all of the subscribers on the ResponseReceived event.
The subscribers (possibly hundreds of them) only care about data associated with their particular id. The subscribers look something like:
_matchingResponseReceived = new ManualResetEventSlim(false);
_listener.WhenResponseReceived += checkTheIdOfWhatWeGetAndSetTheEventIfItMatches;
postTheMessage();
_matchingResponseReceived.Wait(someTimeout);
It's that last line that's bugging me. We post the message but then block the whole thread waiting for the Listener to get a response and call our event handler. We'd like to use Tasks but doesn't seem like it'll give us much if we're blocking a whole thread waiting for the callback.
Is there a better (more TPL friendly) way of achieving this so that no threads are blocked and we get fire off more requests simultaneously?
async-await together with TaskCompletionSource pretty much were made for this.
The sender side creates a TaskCompletionSource, adds it to a dictionary (with key being the id of the request), makes the request and returns the TaskCompletionSource's Task.
The receiver then looks into the dictionary to find the right TaskCompletionSource, removes it from there and sets its result.
The caller of the sender method will await the returned Task, which will asynchronously wait for the receiver to process the callback call.
In code, it could look something like this:
// TODO: this probably needs to be thread-safe
// you can use ConcurrentDictionary for that
Dictionary<int, TaskCompletionSource<Result>> requestTcses;
public async Task<Result> MakeApiRequestAsync()
{
int id = …;
var tcs = new TaskCompletionSource<Result>();
requestTcses.Add(id, tcs);
await SendRequestAsync(id);
return await tcs.Task;
}
…
var result = await MakeApiRequest();
var context = await _httpListener.GetContext();
// parse the response into id and result
var tcs = requestTcses[id];
requestTcses.Remove(id);
tcs.SetResult(result);
This whole architecture seems to be more complicated than it should be (I might have not understood your program right).
Why not post your request to the second server (BTW, you don't need string literal for "POST") and end the routine, then get the request from that server in a regular Web API method, parse the data to find the IDs, and execute thread for each ID?

Categories