Lost messages over XMPP on device disconnected - android

I'm trying to develop a turn base game over XMPP. ( The only solution I found for multiplatform game ). I can send messages without problems. If the other user isn't online, the server (OpenFire) save it for later deliver.
The problem cames when a device change the network ( change from 3g to WiFi, change 3g IP... ) or the device lost the network ( turn off 3g, wifi or lost connection ). The server thinks that the device is online and send the message but it ( obviusly ) never arrive, so packet is lost.
I know one solution. Implement ACK over my game protocol, but I don't like this idea to much. Do you have any other suggestion? I think this is a server problem. Do you know another server witch implements TCP or ACK?
Thank you!!
EDIT: I do that: Connect device to server. I turn down the 3G and WiFi connectivity to the device. Android and the server still thinking that the connection is alive.
http://issues.igniterealtime.org/browse/SMACK-331
PD: I ask to openfeint for they multiplayer api, but they didn't asnwer me...

Although BOSH will likely work in this case, another option is XEP-0198: Stream Management. This will allow you to have all of the performance of a fully-connected socket, along with quick reconnects, positive acking, and queuing while un-acked or disconnected in both directions.

Under some conditions TCP/IP is not reliable. This is why ACKs, message receipts, IQs or other extensions in XMPP can solve this problem.
I have done lots of mobile programming over the years, also often with Openfire. But I have not seen lost messages. So I assume that there is a problem in either the library you are using on Android, or the Openfire version you use.
Instead of using raw sockets you can also use BOSH:
http://xmpp.org/extensions/xep-0124.html
BOSH is based on WebRequests like Comet and works very well in environments where you switch or loose often the connection. It can keep the connection alive until your network is back and does not result in connection drops when one or more requests fail in a row.

I too came across this issue and been trying to figure out a proper way to get this resolved.
Problem for me is that I set the offline messages policy to "Always Store" and thus XEP-0184 doesn't really help out to determine if a message is not getting delivered to its receiver.
Providing this scenario:
- I have 2 users chatting, call them A and B
- A sends B a message while B's connection just got lost
- The message got dropped and A is not notified
- In this case A does not know that the message got dropped, it'll just assume that the message is delivered to the server, server will eventually deliver it to B
- B lose the message forever
So I temporarily put in a work around for this... i store all those messages that are not delivered (i.e. haven't receive the message delivery receipt) into a queue, then periodically (say, 6 minutes - it's the time when those dead connections got wiped) check every message of the queue to see if the intended recipient is "Online" AND the receipt's still not received... If it's the case, then I mark that message as "Failed delivery"
This is quite a terrible way to fix it (please advise if you have a better way of doing it). I think the best thing to do is to have the server to do this: if message failed to deliver and the offline message policy is "Always Store", then we store it to "ofoffline" for delayed delivery.

This is an old one, but I was solving such an issue recently. It helped me when I set the XMPP resource (the last part of full JID) to something reasonable, when building connection. Otherwise it will be random generated on each reconnect - and that changes the full JID.

Related

How to force Openfire to request ack when sending message to clients and if none, returned save as offline

I hope you are having a nice day. We waited a long time for Openfire to implement the StreamManagement capability, and now we have it running. However, the real problem scenario of packet loss in Openfire still remained the same.
The client disconnects from the internet but since it was before Openfire sent its next ping, the server assumes that the client is connected and sends the message, which apparently happens without requesting ack from the client. So, if it didn't send any, save it as offline.
I have tried many things to fix this (played around with StreamManagement and _Resumption_) and the last thing I can think of is to write a plugin in the server side and do everything about storing offline messages, manually, which is going to take a long time. But I still imagine this should have a solution, otherwise, Openfire is useless, isn't it? Can someone please help me if there is any solution to this? Thanks a lot.
Plus, I wonder, doesn’t Openfire use TCP to stay alive? So, shouldn’t it notice whenever the client disconnects instantly?
My way of initiating the connection with smack:
connInter.getConnection().setReplyTimeout(15000);
connInter.getConnection().setUseStreamManagement(true);
//connInter.getConnection().strea
connInter.getConnection().setUseStreamManagementResumption(true);
registerReceiver(mConnReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
DeliveryReceiptManager
.getInstanceFor(connInter.getConnection())
.setAutoReceiptMode(AutoReceiptMode.always);
ReconnectionManager.setEnabledPerDefault(false);
SASLMechanism mechanism = new SASLDigestMD5Mechanism();
SASLAuthentication.registerSASLMechanism(mechanism);
// SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1");
SASLAuthentication.unBlacklistSASLMechanism("DIGEST-MD5");
PingManager.setDefaultPingInterval(180);
This is a BUG check it here:
https://issues.igniterealtime.org/browse/OF-963
This is official Bug in Openfire. You can set time interval after which user is set to offline. But in versions > 4.0.2 there is bug where client is always online regardless the time interval u set. I use older version exactly 4.0.2 for my Openfire installation as this BUG is not fixed yet.
This is the reason why messages are sent from another client and lost, cause first client appear as online and in reality it is not and message never get receipt for delivery.

Android - How to know if a TCP connection is still opened

I'm doing an application that will comunicate with a TCP server. I based my work on the following example http://myandroidsolutions.funcode.ro/2013/03/31/android-tcp-connection-enhanced/.
Right now I'm looking how to verify if there is a connection between Client and Server. The mBufferOut.checkError() only give an error after 2/3 tries. I need to know if my message was sent or not.
Thank you
In network communications, you are never sure when (or if) the remote party received the message in full, in part, or at all.
If the connection is broken, then you know for certain that message was not received in full.
But if you need a timely confirmation, the server should send some confirmation message. Your application protocol needs to support this, though.
The same is valid to know if the connection is "up" or "down": TCP does not exchange any packets when idle, unless you activate keep-alive (which is not normally recommended). It may well happen that the party thinks it has a connection, only to send a message and then it fails (perhaps because a router was reset while the connection was idle).
To solve the problem we need to look at the return value of readLine(). If it return null so our tcp connection was closed.

Is a live internet connection required for push notification?

This maybe a silly question, but I need confirmation and I have no one else to ask.
I am trying to understand the implications of implementing my own push notification for android mobile devices. This requires a continuous TCP connection to a server, though most of the time it will be idle.
My assumption is that, even when idle, for the server to be able to push data to the client through the TCP connection, an active internet connection will always be required, and if the connection is disrupted (i.e. the user switches the connection off) this push will no longer be possible.
Is this assumption correct ?
I'd say yes, it's correct indeed. How would you communicate without a live internet connection?
Afaik, Android C2DM handles this by queueing up the push requests when the user does not have a live net connection and sending them down to the user when the connection gets back up. You could implement a similar behavior in your solution's server side.
Answer is YES
To understand the best way then any-other about C2DM, You just should go through this Google Project C2DM
They have provided complete documentation required to Understand the work and mechanism of C2DM and provided completed Examples too.
I have one another favourite tutorial for the same is : Vogella's
You just need to give a good time to read this thing and implement..
Happy Coding :)
You are right. C2DM maintains a open socket (with Market or Gmail app), which it uses to identify your device.
And ofcourse, you will need Wifi or a cellular network to receive the push notifications.

Reliability of C2DM

I am having issues with C2DM. Sometimes works perfectly, sometimes my messages simply do not get pushed. Is there reliable way to enforce this connection? To pull messages. I read somewhere that what google do is keep low bandwidth TCP connection to their server at all time. So I assume that
when switching between network types TCP connection falls down and Android tries to reestablish connection to C2DM servers. So that might fail on WiFi with restricted network. Is that wrong assumption?
I have noticed with WhatsApp that on WiFi sometimes I do not get messages. When I switch to 3G I usually get them at the moment of the switch. What tips from your experience with C2DM would you offer?
C2DM is not suitable for critical parts of your application, since Google currently does not offer an SLA or paid tiers that will guarantee you reliable service and throughput.
I've considered several alternatives myself: XMPP via asmack, Parse, Deacon, Urban Airship, and MQTT.
After some reading and experimenting I decided to go with MQTT. It is a very lightweight telemetry protocol invented at IBM that fits quite nicely in the Android push notifications scenario. I recommend you give it a try, here's a nice blog post to guide you: Using MQTT in Android mobile applications.
Hope this helps.
C2DM does not guarantee that your message will be delivered, and your application should not assume that in order to work correctly. Therefore, your C2DM message should never contain the data itself but, rather, a notification that there is data available. In other words, the loss of a C2DM message should never cause your application to lose data; it should, at most, cause it to take longer to notice that a certain piece of data is available on your server.
A typical app should connect to its server once in a while (a long while) to retrieve messages, even when using C2DM, to cover the case where C2DM messages might not be delivered.
Depending on network configuration, the device might not be able to receive C2DM messages; restrictive firewalls or other strange WiFi configs might do that.
With C2DM reliability is not a guarantee. So best to have a ACK
message or some way in which you (the sender) the realize the message was received successfully.
Also make it a point to override onRegister class properly because the device Reg ID keep on shuffling.
Lastly, if you are planning to send updates regularly, I'd prefer polling to C2DM just because of the amount of requirements to get it functioning while reliability and ultimate control is still not assured.
I've been struggling with the same problem myself. The behaviour you describe is accurate. I'm developing an app that uses c2dm mainly with Wifi connection, and I had to implement an AsyncTask to periodically (minute and a half) call WifiManager.reassociate() (turning wifi off and on again triggers the arrival of all pending notifications, that's what inspired this solution) so I can keep the notification arrival as accurate as possible. Not so sure about the correctness of this practice, though.
Have you tested it every 15 minute connection? I created a schedule task to send the message. I use NotifyMyAndroid to push it. C2DM sometime pushes the message about 10 mninutes after not instantly. But, sometimes you get it in around a second.
The best way you can do this is by testing. I have a mechanism in my app that when I enable debugging I receive a HTTP request from the client saying that they received the message.
I find that this number is about 80%. Fortunately that's enough for the scope of my application.
Wifi shouldn't interfere in the C2DM ability of receive messages. At least while the phone is active.
What happens is that android turns the wifi OFF after the phone is in standby for a while. The messages won't be available at that period of time, simply because there's no internet connection available. Right after the user wakes the phone up, they should receive the messages.
After a long time research pretty much "all the internet" for an answer, I've found it. As I posted before, I was struggling with the problem myself and found out it was not a C2DM problem, or even a implementation problem. It was simply a router or firewall misconfiguration. Android uses a persistent TCP connection with a heartbeat keep-alive mecanism to ensures that the connection stays up. Google uses the state of the connection to determine if your device is idle or not. But if your router has a protection policy that checks for "unused" connections and terminates them, that won't work. Android notifications should be delivered (close to) instantly. I've tested this in my school network and home network, with two diferrent behaviour.
To resume: be sure to check your network configurations.
Some APNs work better than others with C2DM. Google "gtalk apn", for example, to find forums about the impact of the APN on C2DM.

Ways to guess if C2DM is connected

I'm trying to place a best guess estimate as to whether C2DM messages can be received.
I've created an application that relies on pushing information to a phone while it is physically inaccessible. I understand that C2DM isn't guaranteed delivery, but I'd at least like to know when the delivery of a message is even possible; when it isn't we fall back to our own push service (and can actually tell when we're connected).
I've noticed C2DM on android will still issue auth tokens even when there is not a logged in google account; messages still seem to be delivered in this instance even though it's stated that they shouldn't be. If GTalk isn't connected (firewall or other reasons), no response at all is returned when requesting an auth token. Auth tokens are returned to the application when the phone is in airplane mode. This means it's not as simple as checking if an internet is available. I can't find a reliable way of checking if GTalk is logged in.
Again, I don't need to guarantee the delivery of messages, but I'd at least like to know if delivery is even possible. Does anyone have interesting solutions?
Go watch this video, it's a Google I/O talk about C2DM, how to use it and how it works. AFAIK, you can't know if it's connected or not. Probably most of the time they don't even know (until they have to deliver a message and fail).
However, it is highly recommended (in the video as well) that you do not send important data through C2DM (as messages can get lost). The service should only be used as a "network tickle" (with a footprint as small as possible). Your application should be woken up by this tickle and it should start fetching the information it needs itself.
Now, if you implement it this way, it should be easy to implement a polling mechanism. Since you already separated the "tickle" from the actual information retrieval, you can just trigger the retrieval every once in a while if there's no tickle.
Something you can do to check whether C2DM is connected is something like a ping:
Send message to phone via C2DM
The app receives (or doesn't receive) the message and sends a "pong" back to your server
The server waits for the "pong" for a predetermined amount of time (1-2 minutes, I'd say) before marking the device as "offline".
Edit: relying on GTalk is not feasible. GTalk relies on C2DM just like your app, it doesn't have anything "extra". Also, GTalk is not present on all devices. I'm not sure how the GTalk app determines whether it's offline or not (it's not open source, unfortunately), but I'd guess it just tries to ping a server and fails.
No that is impossible.As you device authenticated once and generate registration ID and send to third party server(As you already know).Now your work is over once the device has been registered.So Wait for message either you got or not(No guarantee of delivering message as C2DM used UDP Protocol ).
Alternative Solution
Although its impossible to check from Google side directly as i mentions above,But if you have any urgency to check connection from your phone
then you can take approach like this
Step 1): Make one Web service to check connection
Step 2): Call this web service from application that will command to server to send push notification for checking purpose.
Step 3): Now from server side,server will immediately send push notification for particular device(FROM which it get command)
Step 4): Now if you got push notification that means you are still connected to C2DM.
this will not take much time.But follow it only when checking connection is urgent and it is on user
This may be a bit naive as I am not an active C2DM user, but wouldn't it be possible to read
/proc/net/netstat
and see if there are any active TCP connections. If there aren't any, then C2DM can't possibly be working. You could also make this technique more versatile by forming a C2DM whitelist that you would expect to find (or maybe its possible to filter on a special C2DM port?)
If the device is inaccessible, even your fallback push messaging system wouldn't work. The C2DM doesn't guarantee that it will deliver your message, but the event of non-delivery would be very rare. So would be the case with any other service. The best workaround that you could have is to poll your server to check if you have any new messages that hasn't been delivered yet. I am assuming that your application is such that it's very important not to miss even a single message in 500 or may be 1000. In that case, you could implement a hybrid of push and pull.
I've worked a little with C2Dm, I've created my own push 3rd party server .
I've implemented a little logic based oh C2DM http response code to know if a push message was sent or not .
Here is some of the code I used :
int responseCode = conn.getResponseCode();
if (responseCode == HttpServletResponse.SC_UNAUTHORIZED || responseCode == HttpServletResponse.SC_FORBIDDEN) {
LOGGER.warn("Unauthorized - need token");
return false;
}
here I'm almost sure that the push message was sent from the c2dm servers because I've got an id ont the response:
if (responseParts[0].equals("id")) {
LOGGER.info("Successfully sent data message to device: " + responseLine);
return true;
}
I've used other methods to get other result codes from Google if you want I can post them.I hope that I've helped you a bit .
I don't think there's any way to determine in ADVANCE whether there's any chance a push attempt will work, but I can think of a fairly straightforward way to verify receipt (but not queueing for future delivery via C2DM) -- just complete the message loop.
Remember, C2DM's main benefit is that it allows notifications when then phone is asleep and nominally offline. Once your application gets the notification, there's little to stop you from waking up the phone at that point, bringing up the network, and sending a confirmation. I don't think you'd even have to request "keep phone awake" permissions, because I believe the mere act of having registered for C2DM notifications and receiving one is sufficient to wake up the phone and allow the app to continue running normally (at least, long enough to bring up the network and send the confirmation).
While you're at it, you should keep track of confirmations that happen LONG after you expected them to be a lost cause. If you see more than a few, you might have to alter the resend strategy.
The only real-world edge case where this might fail is if you had users who bent over backwards to disable data while leaving voice/SMS enabled (I'm pretty sure C2DM uses 4 bytes of the response datagram sent when a phone polls for incoming calls & text messages that were originally set aside for RIM, then later repurposed for Apple and Google).
try to shut down all network connections and reconnect again. if you will get an registration id, then you can receive messages.
Something related to the long running C2DM-connection that is used to deliver the triggers:
On WLAN it sends a heartbeat every 15 minutes.
On mobile networks the timeout is 28 mintes.
28 Minutes might be to long, depening on the hardware your mobile carrier uses, 2g/3g repeaters in garages, etc.
You can get lots of information about the connection by opening the Google Talk Service Monitor Application: http://www.honeytechblog.com/monitor-google-talk-service-android/
Dial: ##8255##
Theres also a button that sends a heartbeat right now and resets the timeout.
If you want to ensure (on the client side) that c2dm-messages can be received at a given time, your best bet is to re-send the heartbeat. This can be done programmatically - only on rooted devices though. I might release an apk to the market sometime that does exactly that.

Categories

Resources