Asynchronous event execution using websockets - c#

I have windows application that communicates to server via websockets, I use meteor server as server
and websockets4net for communication in client. when send some messages to server(json format).
server will respond to websocket in different order. I can keep track of messages sent to server.
In c# can we have produce consumer pattern, where I can store messageid and callback in dictionary
and execute callback when response recieved.
//<int, Action>(<messageid,Callback delegate>)
ConcurrentDictionary<int, Action> serverResponseCollection = new ConcurrentDictionary<int, Action>();
public send(JObject message,Action Callback)
{
socket.Send(message);
serverResponseCollection.Add(message.id,Callback);
}
void socket_MessageReceived(object sender, MessageReceivedEventArgs e)
{
//server response contains message id
//get callback function and execute
var callbackObj = serverResponseCollection[e.id];
Callback();
}

You can add a field called "Method" to the json object and set its value to the name of operation you are performing while creating a request message.
message.Add("Method", "GetUser");
The serve will also add "Method" field in the response with the same value the client has sent in the request.
And then in your response handler you can parse the value of Method field to know which operation you need to perform. And you will also get rid from mapping the message ids with callbacks.

Related

How to check status of queue trigger function programmatically in c#?

I have two azure functions, one is HttpTrigger, one is QueueTrigger. The purpose of the http trigger is to get some data from some service and put it in blob storage. The purpose of queue trigger is to get that data from blob storage and save it in local database. When http trigger finish the job, the queue trigger is triggered automatically.
I am wondering how can I know when the queue function is finished programmatically?
So, I have some service which is calling this http trigger function:
public async Task<HttpResponseMessage> CallHttpAzureFunction(int someParameter)
{
var client = new HttpClient();
var azureFunctionsPath = "someRandomPath";
var url = $"{azureFunctionsPath}/{AzureFunctionConstant.NameOfFunction}";
var request = "not important"
var response await client.PostAsync(url, new StringContent(JsonConvert.SerializeObject(request)));
if (response.Result.IsSuccessStatusCode)
{
//Check the status of queue function, if it is finished, call the database to get that data
}
}
Currently, you can't get the status of the azure function but if it is a durable function you can redirect the client to a status endpoint that the client polls to learn when the operation is finished.
REFERENCES:
How to check the status of a Function
Durable Functions Overview
How to check running status and stop Durable function

SignalR Client - Handler for message that contains no event name?

I am coding a SignalR Hub Client to get data from a web-service, and for one of the more important methods I am invoking on their server, the message I get back contains no "M: eventName" line of the sort I am using to handle the other message types (just "R:[Data] and I:[0]"). I can see the message data is all there when I enable Tracing, but how do I go about handling this with no eventName?
example:
`HubProxy.On<DataClass>("???event-name-here???", update => Console.WriteLine("OUTPUT");`
Message Trace:
23:02:34.9119843 - e1ef32d1-d374-4d7d-82b4-011d906cb096 - WS: OnMessage({"R":{"MarketName":null,"Nounce":136096,"Buys":[{"Quantity":1261.89236694,"Rate":0.00567987},{"Quantity":17.66436734,"Rate":0.00565000},{"Quantity":0.35424250,"Rate":0.00564585},{"Quantity":148.53138590,"Rate":0.00564100},{"Quantity":114.03031557,"Rate":0.00564000},{"Quantity":823.55802148,"Rate":0.00563998},{"Quantity":92.11307737,"Rate":0.00563997},{"Quantity":439.17714798,"Rate":0.00563990},{"Quantity":35.46237619,"Rate":0.00563978},{"Quantity":380.42279579,"Rate":0.00563777},{"Quantity":308.83819198,"Rate":0.00563600},{"Quantity":0.10951077,"Rate":0.00563433},{"Quantity":0.35548667,"Rate":0.00562609},{"Quantity":11.23857359,"Rate":0.00562082},{"Quantity":2.69321221,"Rate":0.00562011},{"Quantity":0.99414299,"Rate":0.00561891},{"Quantity":24.41591498,"Rate":0.00561500},{"Quantity":0.35673516,"Rate":0.00560640},{"Quantity":2.66949153,"Rate":0.00560500},{"Quantity":38.31222855,"Rate":0.00560000},{"Quantity":17.84436494,"Rate":0.00559000},{"Quantity":208.91357967,"Rate":0.00557050},{"Quantity":0.89792884,"Rate":0.00556837},{"Quantity":6.28868665,"Rate":0.00556555},{"Quantity":178.86139272,"Rate":0.00556301},{"Quantity":304.80171408,"Rate":0.00556300},{"Quantity":0.56409118,"Rate":0.00556122},{"Quantity":11.57184239,"Rate":0.00556069},{"Quantity":0.19164392,"Rate":0.00555933},{"Quantity":3.00000000,"Rate":0.00555560},{"Quantity":1579.01........
It looks like that message is not a client-invocation (what you are calling an "event") but the return value of a server call.
To use return values; use Invoke<T> instead of Invoke on the IHubProxy object.

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?

Getting the call response from the menu with Twilio

I am using C# to send a phone message with option(Twimlet) for the user to press 1 to confirm. How do I get the response from the call?
The code terminates prior to the call being placed, I assume I need to query the twilio server with the call sid?
static void Main(string[] args)
{
var twilio = new TwilioRestClient(AccountSid, AuthToken);
var options = new CallOptions();
options.Url = "http://twimlets.com/menu?Message=Please%20press%201%20to%20confirm%20or%20Press%202%20to%20cancel&Options%5B1%5D=http%3A%2F%2Ftwimlets.com%2Fmessage%3FMessage%255B0%255D%3DYou%2520have%2520confirmed%252C%2520Thank%2520you%2520good%2520bye.%26&Options%5B2%5D=http%3A%2F%2Ftwimlets.com%2Fmessage%3FMessage%255B0%255D%3DYou%2520have%2520selected%2520to%2520cancel.%2520Thank%2520you.%2520Good%2520bye%26&";
options.To = "+13105551212";
options.From = "+13105551213";
var call = twilio.InitiateOutboundCall(options);
Console.WriteLine(call.Sid);
In order to respond to any input from the call, you need to use URLs under your control. Twimlets are pre-defined "apps" that don't give you control of the call flow outside of what you can specify in URL parameters.
The code you have now terminates because all it is doing is making an HTTP call to Twilio's servers telling it to start the call, with the options.Url endpoint responsible for handling that call's flow. To write a custom flow, you would need to create a public URL that returns TwiML for the desired flow.
Once you've got that going, you'll use the url attribute of the <Gather> verb to indicate where the key press data should be sent.

How to broadcast (push) a message send by one client, to all clients through a server using WCF NetHttpBinding (WebSockets)?

In .NET 4.5 a new WCF binding- NetHttpBinding- has been introduced which uses WebSocket protocol as it's underlying transport. Which implies that this enables a true push from server. Now, I have been able to make some sort of push using A callback contract like this:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class WebSocketSampleService : IDuplexContract
{
public string SayHelloDuplex()
{
//push to the current caller
OperationContext.Current.
GetCallbackChannel<IDuplexCallbackContract>().
SayingHello("Hello from WebSockets");
//answer the current caller in the regular http way
return "Hello";
}
}
[ServiceContract(CallbackContract=typeof(IDuplexCallbackContract))]
public interface IDuplexContract
{
[OperationContract]
string SayHelloDuplex(string name);
}
[ServiceContract]
public interface IDuplexCallbackContract
{
[OperationContract]
void SayingHello(string message);
}
What I would like to do though, is to broadcast the message to all clients when a single client calls the method SayHelloDuplex(). Is there a way to access the callback channels of all clients? Or should I record the callback channels of all the clients for later use in some other method (E.g. Connect())? Perhaps I'm tackling this problem in the wrong way?
Any help will be appreciated. Thanks
Callback channel is unique per client, so there is no way to access the callback channel of all clients.
Instead you should save the callback channel for each client in a list or even better in a dictionary so you can target specific client.
Then when you want to broadcast a message to all clients just go over the list.

Categories