Keep WebSocket and some event handlers alive while app is active - c#

I use Caliburn.Micro to build a Windows 8.1 Universal app. The app connects to a web service using a WebSocket. I would like this connection, once established, to be kept alive as long as the app is active, no matter what page the user is on.
Currently I'm doing it like this:
container = new WinRTContainer();
container.Singleton<IConnectionService, ConnectionService>();
and it seems to work as I want to. I can inject it in my viewmodels and the connection is still open and it does receive messages even when a view model that does not inject the service is active. I am however a bit curious if this is the correct way (and if it's actually doing what I'm expecting)?
Secondly, I'm using the connection manager to parse the JSON returned from the WebSocket connection and creating corresponding classes like RandomThingHappened and broadcasting these using the event aggregator service from Caliburn.Micro. View interested in these can subscribe and do what they want. However, there are some messages that I would like handled regardless of which view the user is on. Is this possible? I've thought about creating singletons for this as well, and just make sure to instantiate these somewhere. That does however seem a bit... risky.
Suggestions?

Not really sure about websocket but
I am using the following ways for My WCF service ( the dumb terminal must be always connected cause the WCF service will push message to connected terminal using callback
[OperationContract(IsOneWay = true)]
void KeepConnection();
and in your client use a timer to keep calling the service
var timer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 50) };
timer.Start();
timer.Tick += (sender, args) =>
{
try
{
if (this.client.State == CommunicationState.Faulted)
{
this.RegisterTerminal();
}
this.client.KeepConnection();
}
catch
{
throw new Exception("Failed to establish connection with server");
}
};
As for the broadcasting you can use EventAggregator to publish your event to all the listening class.
you can read it more here Event Aggregator

Related

SignalR Send method not firing while trying to poll for existing connections

I'm using the latest version of SignalR with jQuery and getting some odd behavior where a user disconnects, but still hangs around in my "UserQueue" despite having disconnected.
I think this may be related to the fact that a page refresh appears to trigger the OnDisconnected and OnConnected events almost simultaneously. When I set a break point in one of these methods and step through, my pointer bounces back and forth between the two methods with each step (F10).
I'd like to run a check with each OnConnected event to find out who is actually connected to my app. I want to fire a JS event from my C# code in the OnConnected event, and then allow the client/front end to fire back a confirmation of the user being present:
public override Task OnConnected()
{
// Check for other users:
Clients.All.checkForOtherUsers();
// Do stuff with my user queue
var curmodelId = GetModelId(Context);
var curUserConnection = GetCurrentUser(Context);
// First create the ledger is missing, setting the modelId as the first key
if (_ledger == null)
{
_ledger = new ConnectionLedger(curmodelId, curUserConnection);
}
else
{
// key not found, create new connection pool this model
if (!_ledger.PoolIds.Contains(curmodelId))
{
_ledger.AddConnectionPool(curmodelId, curUserConnection);
}
else
{
var curModelPool = _ledger.ConnectionPools.First(x => x.Id == curmodelId);
curModelPool.UserQueue.Enqueue(curUserConnection);
}
}
return base.OnConnected();
}
Now in the client JS, if I have this code:
modelHub.client.checkForOtherUsers = function () {
// I can see logging here if I add console.log("checking for other users...")
modelHub.server.send();
}
...I'm expecting my C# Send method to receive back the context (if the user is actually present at the client for the JS to execute) and update my UserQueue:
public void Send()
{
var curmodelId = GetModelId(Context);
var curModelPool = _ledger.ConnectionPools.First(x => x.Id == curmodelId);
var curUserConnection = GetCurrentUser(Context);
curModelPool.UserQueue.Enqueue(curUserConnection);
}
... but my Send method never gets fired, even when I fire it from the JS Console directly $.connection.modelingHub.server.send().
Why doesn't the Send method fire? Is there something about the order of events / async nature of the SignalR library that causes this?
Comments on the overall design are welcome too (since I suspect it could be stupid), but note that I cannot access Context.User or Clients.User because of my customized Authentication config.
Seems to be two causes below.
Server-side
Not good to call Clients.All.checkForOtherUsers(); within OnConnected().
I would recommend calling it after connected.
Please refer to SignalR - Send message OnConnected
Client-side
Might need to regist modelHub.client.checkForOtherUsers = function () { before calling start method.
Normally you register event handlers before calling the start method
to establish the connection. If you want to register some event
handlers after establishing the connection, you can do that, but you
must register at least one of your event handler(s) before calling the
start method. - How to establish a connection

Unacknowledged message on ActiveMQ with fail over setup is not redelivered to subscriber

I am implementing Active MQ publisher and subscriber in c#. I am using Apache.NMS.ActiveMQ .net client library to communicate with the broker.
<package id="Apache.NMS" version="1.7.1" targetFramework="net461" />
<package id="Apache.NMS.ActiveMQ" version="1.7.2" targetFramework="net461" />
ActiveMQ is configured with a fail over setup on 4 servers (.225, .226, .346, .347 - last parts of IP for reference) . The broker url looks something like
failover://tcp://103.24.34.225:61616,tcp://103.24.34.226:61616,tcp://103.24.34.346:61616,tcp://103.24.34.347:61616
Here is how I am publishing
var brokerUrl = "failover://tcp://103.24.34.225:61616,tcp://103.24.34.226:61616,tcp://103.24.34.346:61616,tcp://103.24.34.347:61616";
var connectionFactory = new ConnectionFactory(brokerUrl);
using (var connection = connectionFactory.CreateConnection("conn1", "conn$34"))
{
connection.ClientId = "TESTPUBLISHER";
connection.AcknowledgementMode = AcknowledgementMode.ClientAcknowledge;
connection.Start();
var session = connection.CreateSession();
var topic = new ActiveMQTopic("ACCOUNT.UPDATE");
var producer = session.CreateProducer(topic);
var msg = "43342_test"; //DateTime.Now.ToString("yyyyMdHHmmss_fff") + "-TEST";
var textMessage = producer.CreateTextMessage(msg);
textMessage.Properties.SetString("Topic", "ACCOUNT.UPDATE");
textMessage.Properties.SetString("Action", "UPDATE");
textMessage.Properties.SetString("DataContractType", "Account");
producer.Send(textMessage, MsgDeliveryMode.Persistent, MsgPriority.Normal, new TimeSpan(0, 0, 60, 0, 0));
}
Here is how I am subscribing to the topic. This code is setup such that, multiple shared subscribers can listen for incoming messages. I was told I have to use Virtual Topics to accomplish that. So I configured my subscriber to use Virtual Topic and it is hosted inside a Windows Service project. I am using Acknowledgement Mode to be ClientAcknowledge, so that unless a message is acknowledged, it should keep coming back. Below code snippet is representing just the important subscriber portion of the windows service.
var brokerUrl = "failover://tcp://103.24.34.225:61616,tcp://103.24.34.226:61616,tcp://103.24.34.346:61616,tcp://103.24.34.347:61616";
IConnectionFactory factory = new ConnectionFactory(new Uri(brokerUrl));
IConnection connection = factory.CreateConnection("conn1", "conn$34"))
connection.ClientId = "TESTSUBSCRIBER";
connection.AcknowledgementMode = AcknowledgementMode.ClientAcknowledge;
connection.ConnectionInterruptedListener += OnConnectionInturrupted;
connection.ExceptionListener += OnConnectionExceptionListener;
connection.ConnectionResumedListener += OnConnectionResumedListener;
connection.Start();
ISession session = connection.CreateSession(AcknowledgementMode.ClientAcknowledge);
var queue = new ActiveMQQueue("VT.TESTSUBSCRIBER.ACCOUNT.UPDATE");
ActiveMQTopic topic = new ActiveMQTopic();
IMessageConsumer consumer = session.CreateConsumer(queue);
consumer.Listener += OnMessage;
private void OnMessage(IMessage message)
{
var payload = ((ITextMessage)message).Text;
Log.Info($"Received message for Client TESTSUBSCRIBER - [{payload}]");
if(payload != "43342_test")
{
message.Acknowledge();
Log.Info($"Message acknowledged for Client TESTSUBSCRIBER - [{payload}]");
}
}
private void OnConnectionResumedListener()
{
Log.Info($"Subscriber connection resumed for Client TESTSUBSCRIBER");
}
private void OnConnectionExceptionListener(Exception exception)
{
Log.Error(exception);
}
private void OnConnectionInturrupted()
{
Log.Error($"Subscriber connection interrupted for Client TESTSUBSCRIBER");
}
I am able to publish and subscriber messages. I am running into an issue with one particular case. Lets say the subscriber established connection to the (.225 broker server) from the pool of fail over servers. The publisher published a message. Subscriber received it and it is in the middle of processing. But due to some server patch maintenance, the windows service had to shutdown. As a result the subscriber connection to the broker got disconnected. When windows service came back up, this time subscriber established connection to a different broker server (.346 broker server) from the fail over pool. When that happened, the unacknowledged message never got re-delivered. But If I restart the windows service and by any luck If connection was established to .225 broker (same server to which subscriber was originally connected to), now the subscriber receives the unacknowledged message.
My assumption is that when ActiveMQ is configured in a fail over setup, no matter which broker server from the fail over pool the subscriber is able to establish a connection to, it should always receive the unacknowledged messages.
In some situations fail over setup appears to be working. Lets assume the subscriber was connected to .346 broker server from the fail over pool. The publisher is connected to different broker server (.225 broker) from the same pool and publish a message, subscriber is receiving messages. This proves that Fail over setup is working.
But once a subscriber receives a message from a broker server and if the subscriber is disconnected prior to acknowledging the message, it has to reestablish connection to the same broker server to receive the unacknowledged message. This does not sound right to me.
Is there any additional configuration required on the Active MQ server setup to make this use case work?
The solution to this problem was not on the client side, but rather with the Active MQ server configuration.
For Producer flow control destination policy, add ConditionalNetworkBridgeFilter and enable replayWhenNoConsumers.

Simple.OData BeforeRequest event not triggered

I'm using the Simple.OData adapter to try to connect to an OData service. The service needs authentication to connect.
I have registered the BeforeRequest event to set the neccesary headers before doing any request.
However, my BeforeRequest is not triggered at all which results in not being able to open the Context, due too missing credentials and my code hangs and waits forever.
See my code below, am I missing something?
public void GetData()
{
var oDataFeed = new ODataFeed(ApiBaseUrl);
oDataFeed.BeforeRequest += BeforeRequest;
oDataFeed.AfterResponse += AfterResponse;
Context = Database.Opener.Open(ApiBaseUrl);
// do some more
}
private void BeforeRequest(HttpRequestMessage httpRequestMessage)
{
// add headers.
}
It did seem to fire or trigger the event once, however, after a rebuild of the project it doesn't work anymore.
There is a know bug in Simple.Data.Client 3x that affects request interception in certain scenarios. The bug is fixed in the forthcoming version 4 of Simple.OData.Client, currently available as pre-release but it's very stable and comes with tons of new features including support for JSON payload and OData protocol V4.

Windows Phone 8 push notification push channel always creates new channel uri

I wanted to check that my push notification implementation is correct.
Each time I open my app (in actual fact I register the push channel only on a specific page so it's each time I go back and forth from that page) a new push channel URI is created which I store in my mobile services database to send push notifications to. This doesn't seem correct to me as each time the app/page is opened a new push channel URI is generated and so the list of channel URIs just grows and grows for each device that uses my app. I'd assume that you create a push channel, store the channel URI and push to it as needed. I will make note here that I am using raw push notifications.
I understand that push channels will expire every so often but for me it's occurring each time I back out of the app/page and therefore when onNavigateTo is called I find the push channel which does exist and a new channel URI is always created. Is this correct?
My code is as follows:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
registerPushChannel();
}
private void registerPushChannel()
{
// The name of our push channel.
string channelName = "RawSampleChannel";
// Try to find the push channel.
pushChannel = HttpNotificationChannel.Find(channelName);
// If the channel was not found, then create a new connection to the push service.
if (pushChannel == null)
{
pushChannel = new HttpNotificationChannel(channelName);
// Register for all the events before attempting to open the channel.
pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);
pushChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(PushChannel_ErrorOccurred);
pushChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(PushChannel_HttpNotificationReceived);
pushChannel.Open();
}
else
{
// The channel was already open, so just register for all the events.
pushChannel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(PushChannel_ChannelUriUpdated);
pushChannel.ErrorOccurred += new EventHandler<NotificationChannelErrorEventArgs>(PushChannel_ErrorOccurred);
pushChannel.HttpNotificationReceived += new EventHandler<HttpNotificationEventArgs>(PushChannel_HttpNotificationReceived);
// code which passes the new channel URI back to my web service
}
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
pushChannel.Close();
}
So to clarify, the app is opened and the push channel is registered and the channel uri is saved in my web service. The web service then sends notifications to the channel uri. When I exit the app or page and return to it, the push channel is found but a new channel uri is created which I again save to my web service. My channels table in effect just keeps growing and growing.
So is this the way it should work with new channel URIs continually generated? It kind of doesn't make sense to me. I'm not sure how toast and tile notifications work but I'd assume the channel URI needs to be static when the app closes to keep receiving notifications while the app is closed, but perhaps that could be a functionality of bindtotoast and bindtotile and so what I'm doing is correct because it's to do with raw notifications.
You're mostly doing it right.
Push Notifications are a funny thing.
You create a channel, send it to your server and then the server can send until it fails (the channel Uri expires or there's an error).
At which point the app needs to create a new ChannelUri and then UPDATE the value stored for that app/device on the server. The server will then be able to send notifications.
Some important points
When a new channel Uri is requested for one that is still valid you'll get the same one back.
When your ask for a new channel uri and the current one has expired, you'll normally get the same uri returned but the channel will be made live again.
There is no way to know if a channel has expired from within an app without running code like your registerPushChannel method. (Unless you track this on your backend and the app queries the backend.)
There is no way to tell the app that a channel has expired, or tell the user to reopen the app to re-establish a channel connection using the push infrastructure.
The standard way to try and ensure that the channel is always available is to check the channel whenever the app is started.
This is what you're doing, you probably just want to make sure you're updating server records not just adding more.

SignalR in SL5 stops working after upgrade to 1.0.1

I have a simple Hub that was working with 1.0.0-rc2 between my web app and my SL5 application.
After upgrading to 1.0.1, my .Net Winform clients can still connect to the hub, but my SL5 client can no longer connect.
Upon calling Subscribe, I can see the Negotiate call in Fiddler (which returns 200, with what appears to be valid JSON), followed by the hub Closed event firing. The Error event never fires, and then that's the end of it. There is no attempt to actually connect, at least as far as I can tell in Fiddler.
Any ideas? Here's my SL5 code, it's pretty simple/straight forward.
hub = new HubConnection("http://localhost/ADE");
hub.Closed += new Action(hub_Closed);
hub.Error += new Action<Exception>(hub_Error);
hub.Reconnected += new Action(hub_Reconnected);
hub.Reconnecting += new Action(hub_Reconnecting);
hub.StateChanged += new Action<StateChange>(hub_StateChanged);
hubProxy = hub.CreateHubProxy(hubName);
//Removed my hubProxy.On calls for brevity.
hub.Start().ContinueWith(task =>
// {
//});
Try this sample that is using SignalR1.0.1 and SL5.
To access SL5 client navigate to http://localhost:18628/SignalR.Sample.SilverlightClientTestPage.aspx
On Fiddler, you should get these http requests
http://localhost:18628/signalr/negotiate?noCache=cd92304d-c824-4c91-abdd-e77c8d096b58
http://localhost:18628/signalr/connect?transport=serverSentEvents&connectionToken=rse-NdoiZ1Hi7riN_beL6J-zrkGMPm1A9p8urTZn_1sTuZVeEzVWayykaN-Km_fmqBoV06D1e7h5fPWl4kgggABs3x7wiItdd8zJKE9FSCjZsYEUQOdFrzAg-WmA7rUx0&connectionData=[{"Name":"SampleHub"}]&noCache=0c4c1fa1-9aa9-409b-acf7-165faa0d699a
http://localhost:18628/signalr/send?transport=serverSentEvents&connectionToken=rse-NdoiZ1Hi7riN_beL6J-zrkGMPm1A9p8urTZn_1sTuZVeEzVWayykaN-Km_fmqBoV06D1e7h5fPWl4kgggABs3x7wiItdd8zJKE9FSCjZsYEUQOdFrzAg-WmA7rUx0

Categories