PushSharp ApplePushChannel not connecting or sending - c#

I'm trying to upgrade from APNS-Sharp 1.0.4.4 and I'm using the current NuGet Version 2.2.1.0
After registering all events, the channel and sending a notification, nothing happens. No event fires, even if I wait for minutes. A test with GCM two weeks ago worked, just the ios side seems to have problems.
code shortened:
push = new PushBroker();
push.OnServiceException += push_OnServiceException;
// shortened.. all On* events registered here.
push.OnDeviceSubscriptionExpired += push_OnDeviceSubscriptionExpired;
push.RegisterAppleService(
new ApplePushChannelSettings(
config.Production,
config.Certificate,
config.CertPassword
)
);
...
AppleNotification anot = new AppleNotification()
.ForDeviceToken(token)
.WithTag(id);
if (alert != null) anot.WithAlert(alert);
if (sound != null) anot.WithSound(sound);
if (badge.HasValue) anot.WithBadge(badge.Value);
if (custom != null) anot.WithCustomItem("cc", new object[1] { custom });
notification = anot;
push.QueueNotification(anot);
...
Console.WriteLine("Press ENTER to stop server");
Console.ReadLine();
...
push.StopAllServices(true);
All the events write to a log if they're called, but none is called.
The Certificate works - I'm getting an error if I test it with the wrong password, none with the right.
If I try to send the same test Notification with APNS-Sharp and he same certificate/token, I receive it on the phone.
Any Ideas?

Related

C# Pusher Client - unable to receive any private Channel events

I have an app that connects to a private Pusher channel and listens to events.
The code looks a bit like this:
// Handle Pusher.
_pusher = new Pusher(Settings.Default.PusherAppKey, new PusherOptions()
{
Cluster = Settings.Default.PusherCluster,
Encrypted = false,
Authorizer = new HttpAuthorizer("[REDACTED]")
{
AuthenticationHeader = new AuthenticationHeaderValue("Bearer", $"{TokenHelper.Token}")
}
});
await _pusher.ConnectAsync();
// This works fine - it says I am subscribed to the channel.
_pusher.Subscribed += (sender, channel1) =>
{
Debug.WriteLine("Subscribed to channel " + channel1.Name);
};
// Never any errors.
_pusher.Error += (sender, error) =>
{
Debug.WriteLine("Pusher error: " + error.Message);
};
// Ok - channel is subscribed.
var channel = await _pusher.SubscribeAsync($"private-[REDACTED]");
channel.Bind(#"App\Events\FileProcessedEvent", PusherEventListener);
It seems like I am subscribing to the channel correctly - the Subscribed event is fired, and I can see that the channel is subscribed (channel.IsSubscribed == true).
However, I am unable to receive any events, including the ones provided by Pusher:
// Never happens.
channel.Bind("pusher:subscription_succeeded", (data) =>
{
Debug.WriteLine("Subscription succeeded.");
});
// Never happens either.
channel.Bind("pusher:subscription_error", (data) =>
{
Debug.WriteLine("Subscription error.");
});
It seems that the event in question ("App\Events\FileProcessedEvent") is fired server-side, but I am unable to receive it, just like the other events. What could be the reason, and how can I fix it?
I have tried Binding the events on the Pusher instance itself, instead of channel, but I failed. I have also tried Binding to all events, but that handler was never invoked - seems like not a single event is captured.
I would also like to mentioned that the channel is authorized properly by the provided endpoint, and that the channel is subscribed with no errors.

PingReply is null when using SendAsync

I am building a server selection screen and have a bunch of IP addresses that need to be pinged in order to know the remote hosts are reachable and to establish a TCP connection afterwards to request their data (like connected playerCount and MOTD, think of Minecraft Multiplayer Selection screen, you don't connect to the game, only requesting data from the endpoint).
The problem is that Ping is always unsuccessful, because the PingReply is null.
For generic use cases, I have wrapped the call with an event that gets fired when the Ping Reply is received back to the sender.
public static async void PingNET(string address, int timeOutMs, System.Action<PingReply> onCompleted)
{
await Task.Run(() => { //to continue unity main thread, start new thread
AutoResetEvent waiter = new AutoResetEvent(false);
var pingSender = new System.Net.NetworkInformation.Ping ();
// call handler when PingCompleted event is raised
pingSender.PingCompleted += (sender, e) =>
{
Debug.LogError ("Error and reply is always null! See: Error is null?: "
+ (e.Error == null) + " - Reply is null?: " + (e.Reply == null));
// If the operation was canceled, display a message to the user.
if (e.Cancelled) {
Debug.LogWarning("Ping canceled.");
}
// If an error occurred, display the exception to the user.
if (e.Error != null) {
Debug.LogError ("Ping failed:" + e.Error.ToString ());
}
if(onCompleted != null)
onCompleted(e.Reply);
};
// Send the ping asynchronously (uses internal new thread,
// so program does not freeze until reply received)
pingSender.SendAsync(address, timeOutMs, waiter);
});
}
Unity Test Runner Unit Test:
[Test]
public void PingTest(){
List<string> addresses = new List<string>(){
"127.0.0.1", "localhost", "www.stackoverflow.com", "www.google.com"
};
for (int i = 0; i < addresses.Count; i++)
{
Ping(addresses[i]);
}
}
private void Ping(string address){
NetworkUtils.PingNET(address, 10000, new System.Action<PingReply>((PingReply reply) => {
if (reply != null && reply.Status == IPStatus.Success)
{
Debug.Log($"Address: {reply.Address.ToString()}");
Debug.Log($"RoundTrip time: {reply.RoundtripTime}");
}
else //this gets called obviously
{
Debug.LogError("PING REPLY ERROR " + reply?.Status);
}
}));
}
Side Info: (Not directly tied to the question)
The reason why I use Ping in the first place is to prevent freeze (deadlock) on the client when the server goes offline while the connection is still tied to it and requesting data to the now unavailable remote host endpoint of the server. I could notify all connected clients that the server is shutting down, but this of course will not work when the server immediately lost connection to the internet.
So all in all, when the ping fails, it should handle in the UI logic to display the server is unreachable. Otherwise if it was successful, a TCP connection to the server is created to send a request. The server that listens on the same port sends a response with the data (playerCount, MOTD, etc.). After that, the client deseralizes the byte array to the datatypes and fires an event, which is subscribed by the UI to gather the deserealized data and display those.
The sending/receiving part with UI already works, just the ping not.
Is this process overcomplicated? Let me know if it could be simplified.
Any help is greatly appreciated.

WNS PushNotificationReceived does not intercept toast push notification

I'm writing a windows desktop app that relies on notifications to work. However, the event handler code, PushNotificationReceived on the channel does not seem to actually fire when I receive a notification. The following code is called to get the channel before its uri is sent to my server:
internal async Task<PushNotificationChannel> GetChannel()
{
PushNotificationChannel pnc;
try
{
pnc = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
if (_channel == null || !pnc.Uri.Equals(_channel.Uri))
{
_channel = pnc;
_channel.PushNotificationReceived += OnPushNotificationReceived;
Debug.WriteLine(_channel.Uri);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
_channel = null;
}
dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;
return _channel;
}
Such that anytime the channel is created or updated (via a different channel uri), it should assign the new channel's PushNotificationReceived event to the following (which is basically lifted from msdn's example):
void OnPushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs e)
{
string typeString = String.Empty;
string notificationContent = String.Empty;
switch (e.NotificationType)
{
//
//other notification types omitted for brevity
//
case PushNotificationType.Toast:
notificationContent = e.ToastNotification.Content.GetXml();
typeString = "Toast";
// Setting the cancel property prevents the notification from being delivered. It's especially important to do this for toasts:
// if your application is already on the screen, there's no need to display a toast from push notifications.
e.Cancel = true;
break;
}
Debug.WriteLine("Received notification, with payload: {0}", notificationContent);
string text = "Received a " + typeString + " notification, containing: " + notificationContent;
var ignored = dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
MainPage.Current.ClearBanner();
});
}
Importantly, "MainPage.Current" is a reference to the app's main page as a static variable. The clear banner line simply removes a pink banner from the main page (just trying to get something simple working to start).
However, the code never seems to fire (no debug statement, pink banner remains). I am successfully getting the toast notification, and clicking on it will set focus to my app, so it's definitely not going to the wrong place.
Is there something I am doing wrong or some way to debug the notifications themselves?

SQLDependency.Stop() - called but when QN Subscription times out, a message is dropped on the queue

Scenario
Static Service Broker Queue and Service
Use these static queues for SQLDependency subscription
Rough outline of code
Using this blog post as a template the code roughly follows this pattern
SqlDependency.Start(this.dbConnectionString, this.notificationQueueName);
Configure Dependency targeting specific service (see code below)
private async void ConfigureDependencyUsingStoreProcedureAndSpecificQueue()
{
if (null != this.sampleSqlDependency)
{
this.sampleSqlDependency.OnChange -= null;
}
if (null != this.sampleSqlCommand)
{
this.sampleSqlCommand.Dispose();
}
if (null != this.sampleSqlConnection)
{
this.sampleSqlConnection.Dispose();
}
this.sampleSqlDependency = null;
this.sampleSqlCommand = null;
this.sampleSqlConnection = null;
//// Create connection.
this.sampleSqlConnection = new SqlConnection(this.dbConnectionString);
//// Create command.
this.sampleSqlCommand = new SqlCommand { Connection = this.sampleSqlConnection };
this.sampleSqlCommand.CommandType = CommandType.StoredProcedure;
this.sampleSqlCommand.CommandText = this.notificationStoredProcedure;
this.sampleSqlCommand.Notification = null;
//// Create Sql Dependency.
this.sampleSqlDependency = new SqlDependency(this.sampleSqlCommand, "service=" + this.notificationServiceName +"; Local database=" + this.databaseName, this.notificationTimeout);
this.sampleSqlDependency.OnChange += this.SqlDependencyOnChange;
await this.sampleSqlCommand.Connection.OpenAsync();
await this.sampleSqlCommand.ExecuteReaderAsync(CommandBehavior.CloseConnection);
if (null != this.sampleSqlCommand)
{
this.sampleSqlCommand.Dispose();
}
if (null != this.sampleSqlConnection)
{
this.sampleSqlConnection.Dispose();
}
Handle SqlDependencyOnChange event as below. Calling the ConfigureDependency code again
private void SqlDependencyOnChange(object sender, SqlNotificationEventArgs eventArgs)
{
if (eventArgs.Info == SqlNotificationInfo.Invalid)
{
Console.WriteLine("The above notification query is not valid.");
}
else
{
Console.WriteLine("\nNotification Time: {0}", DateTime.Now);
Console.WriteLine("\nNotification Info: " + eventArgs.Info);
Console.WriteLine("Notification source: " + eventArgs.Source);
Console.WriteLine("Notification type: " + eventArgs.Type + "\n");
}
switch (optionSelected)
{
case "1":
this.ConfigureDependencyUsingStoreProcedureAndDefaultQueue();
break;
case "2":
this.ConfigureDependencyUsingStoreProcedureAndSpecificQueue();
break;
case "3":
this.ConfigureDependencyUsingTextQueryAndDefaultQueue();
break;
case "4":
this.ConfigureDependencyUsingTextQueryAndSpecificQueue();
break;
}
}
Upon app shutdown call SqlDependency.Stop(this.dbConnectionString, this.notificationQueueName);. This returns true which according to the documentation means the listener was completely stopped.
Issue Faced
What I then see is that when the subscription reaches it's timeout period, it fires and drops a message onto the dependency queue waiting to be consumed.
If these messages stay in the queue, on next startup the app throws The given key was not present in the dictionary.
Also if I call SQLDependency.Stop() and leave the app running, it still consumes the QN fires for timeouts.
What step am I missing here as I am likely to face issues if messages are getting dropped on the static queue causing the The given key was not present in the dictionary exception.
Thanks
This returns true which according to the documentation means the listener was completely stopped.
This makes no promise with regard to the server state, nor the queue state. These will outlive your volatile application state, and at the next startup, you will find notifications from when your app was offline/shut down. You will have to code accordingly, with this expectation in place (eg. ignore keys that are not in the dictionary).
Note that even if SqlDependency.Stop() would attempt to stop the pending subscribed notifications, it is impossible to guarantee success as there could be notifications in transit (ie. pending delivery via Service Broker, notification may be in sys.transmission_queue). Waiting for all in transit notifications to drain delivery is also not feasible.
Additionally, your application would not handle backup-restore scenarios, as it is now. A restored backup may contain pending notification requests which will be invalidated upon restore and fire the notification message. When the application connects, it will find those unexpected notifications.
And ultimately, the application would not be able to handle its own disconnects/crashes. If you are relying on SqlDependency.Stop() to succeed before you can start successfully next time, you won't be able to start at all if you could not call SqlDependency.Stop() (=> any non-graceful stop).
For all these reasons, you must be able to handle keys not in the dictionary.

Azure Service Bus SubscriptionClient.OnMessage always completes message when it shouldnt

I am trying to receive all messages for a given subscription to a Service Bus Topic, but for the context of this app I do not want them dead lettered at this time, I just want to view them and leave them on the subscription. Despite instantiating the Client as
SubscriptionClient sc = SubscriptionClient.CreateFromConnectionString(connectionString, sub.topicName, sub.subscriptionName, ReceiveMode.PeekLock);
and making sure that I am using message.Abandon() rather than message.Complete() the message always gets Dead-lettered after accessing the message. I also have options.AutoComplete set to false
full method code below:
public List<ServiceBusMessage> RetrieveSubscriptionMessages(Subscription sub) {
ServiceBusMessage sbm;
List<ServiceBusMessage> list = new List<ServiceBusMessage>();
String connectionString = ConfigurationManager.AppSettings["Microsoft.ServiceBus.ConnectionString"].ToString();
SubscriptionClient sc = SubscriptionClient.CreateFromConnectionString(connectionString, sub.topicName, sub.subscriptionName, ReceiveMode.PeekLock);
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
sc.OnMessage((message) => {
try {
sbm = new ServiceBusMessage() {
topicName = sub.topicName,
messageText = message.GetBody<String>()
};
list.Add(sbm);
message.Abandon();
}
catch (Exception) {
message.Abandon();
throw;
}
}, options);
return list;
}
Am I missing something ? Or is there an issue with auto dead-lettering with the onMessage() method?
Thanks !
When a message is abandoned the service bus will immediately make it available for re-delivery to any subscriber of the topic.
If you are trying to configure a multicast mechanism in which multiple listeners all receive the same message, then understand that all listeners on a given subscription will be competing for the same message. In order for every listener to receive its own copy of the message, then simply create a unique subscription to the topic for each listener.
If your intent is to delay re-delivery of the abandoned message, you might look at the SO question: What's the proper way to abandon an Azure SB Message so that it becomes visible again in the future in a way I can control?

Categories