Queuing Messages When Internet Not Available in XMPP - android

In my chat application, when I am sending messages/chats to the sender , I am checking the internet connection and the XMPP connection. If both are available, then only I allow the user to send the chat messages.
E.g -
if(connection.IsConnected){
//code to send the message to the sender
}
I am using Openfire Server.
I want to implement a functionality that when the internet is not available and when the user wants to send the message, it should send (i.e the messages should be added in the queue) and should send to the receipient as soon as internet is available again , just as Whats App.
Thanks

Your server is irrelevant in this case if you have no internet/XMPP connection. You just want to buffer the messages in your client and send them once your connection is reestablished.
I will assume you are using Smack or asmack, which means you will want to simply create a connection listener on your connection so that you can send buffered messages when the connection connection is reestablished.
connection.addConnectionListener(new ConnectionListener()
{
:
public void reconnectionSuccessful()
{
resendFromBuffer();
}
});

Related

What is the correct way to save data that will be sent from server to client via socket after client reestablish its lost connection

I have .net core written api and android mobile client. There is signalr socket connection between two of them. When user opens an event on mobile app, i save this event data to db on server and send it to other users that listens the event creator via socket.
I periodically control the socket connection on mobile app and i can reconnect them if they have lost their connection. I have no problem so far.. But i have problem keeping the data for lost connection user
Let me explain with example :
There are Users : Producer1, Consumer1, Consumer2
If Producer1 opens an event via mobile app. It sends data to the server via httprequest and i save it to db.
After saving the data to db, i send this data to Consumer1 and Consumer2 via socket connection.
Lets say Consumer2 dont have network connection while sending the event data.
Consumer1 that have network connection, gets the data successfully and i have no problem for Consumer1.
But, Consumer2 couldn't get the data because it lost their socket connection and we did not reconnect yet.
The sent event data already gone. So Consumer2 will not get the event data.
My question is, how can i keep the data and send it for Consumer2 when just Consumer2 reestablish its connection. What is the efficient and correct way to doing this.
Server Side Code Example :
public async Task AddEvent(GetEventOutput getEventOutput)
{
if(getEventOutput.Active)
{
await _hubContext.Clients.Group(SignalR_EVENT + QUESTION_MARK + EVENT_WITH_USER + EQUAL_DASH + getEventOutput.UserID).SendAsync(CREATE_EVENT_ON_MOBILE, JsonConvert.SerializeObject(getEventOutput));
sendEventCreateNotification(getEventOutput);
}
}
Client Side Code Example :
hubConnection.on(CREATE_EVENT_ON_MOBILE, (getEventOutput) -> {
System.out.println("CREATE_EVENT_ON_MOBILE : ");
setUiAfterSignalrHandler.post(new Runnable() {
#Override
public void run() {
if (!isNullOrEmpty(getEventOutput)) {
Event event = gson.fromJson(getEventOutput, Event.class);
eventsWithTimerRecyclerViewAdapter.insertItem(0, event);
setUiAfterSignalrHandler.removeCallbacks(this);
leftAndRightTextWithTimerRecyclerView.removeEmptyPlaceHolder(eventsWithTimerRecyclerViewAdapter);
}
}
});
restartSocket();
}, String.class);
We had similar situation but with notifications. To avoid your problem, I would recommend to use cache or volatile storage to store those events.
For example, when you send your event to the clients, they should invoke a callback method registering that they received the event, then you register your event as completed when all the subscribers responded the event successfully and you can dispose the event.
But the perform this you should have:
List of all connected clients that wants to receive the event.
Storage for those events (we use Cosmos Db but cache will work well).
So you will not dispose the event until it is completed. But then how you resend the event to the user that was not connected on that time?
Just create a hub method to be invoked by the client just after the connection is establish to send pending events (events that this user has not registered as received, so they did not invoked the callback method).

smack connect to xmpp server with previous stream id

I'm building a chat application using xmpp over Ejabbered for server and smack for android client
I've established connecting , login , send and receiving messages ,then I've faced a problem with user network disconnecting and reconnecting , which has been solved by Reconnecting Manger in smack and xmpp-0198, however there is a case where i need to create a new connection in smack but use the previous session (stream) to get all the messages stored in that session (they don't get stored to offline messages)
,and if i create a new connection with new stream id , user messages get lost .
so is there a connection constructor to implement this solution .
or server side configuration to store thous messages to offline messages
I think one of the following will solve your issue-
First check is mod_offline enabled in the server side.
If mod_offline enabled then check offline message limit in the server side. It should be greater than 0.
Use PingManager to stable your connection. I am here putting sample code to use PingManager in android-
During XMPPTcpConnection initiation-
pingManager = PingManager.getInstanceFor(this.connection);
pingManager.registerPingFailedListener(new PingFailedListener() {
#Override
public void pingFailed() {
// session dropped, request for reconnection
}
});
When XMPPTcpCOnnection authenticated-
#Override
public void authenticated(XMPPConnection connection, boolean resumed) {
configurePingManager();
}
private void configurePingManager() {
pingManager.setPingInterval(ACCORDING_SERVER_PING_INTERVAL);
pingManager.pingServerIfNecessary();
}
Make sure stream_management enabled both in server side and client side. I am here putting a sample code to enable stream_management for android client side-
xmppTcpConnection.setUseStreamManagement(true);
xmppTcpConnection.setUseStreamManagementResumption(true);
When XMPPTcpCOnnection authenticated checking session status send and request all pending streams using the code below-
#Override
public void authenticated(XMPPConnection connection, boolean resumed) {
configurePingManager();
if (!resumed) {
try {
xmppTcpConnection.sendSmAcknowledgement();
xmppTcpConnection.requestSmAcknowledgement();
} catch (SmackException.NotConnectedException | StreamManagementException.StreamManagementNotEnabledException e) {
e.printStackTrace();
}
}
}
Hope following all those steps your problem will be solved.
after lots of searching ,finally i upgraded Ejabberd server to the latest version 17.03
where they've added the new module mod_stream_mgmt ,and changed the behavior of stream management , so when i create a new connection it get rebind to the old one and receive the unsent and un-handled messages
to activated the mod_stream_mgmt i used the following configurations :
mod_stream_mgmt :
resume_timeout :60
resend_on_timeout: true
Note :
I have also activated mod_ping on server side ,I don't know if that has a direct effect on this process and case but right now my clients are not missing any messages .

Communication among all the Nearby devices

I need to implement the communication among all the devices connected to same WiFi AP. I am using the Google Nearby Connections APIs. I am able to connect to multiple device and communicate. But due to some problem connection getting lost. I have checked the Google documentation, It suggest that "When a device is connected to the host, it may send messages to other client devices."
https://developers.google.com/nearby/connections/android/manage-connections
My doubt is what it mean "it may send messages to other client devices".
If multiple client devices are connected to a Host device, then how a client device can send a message to other client devices?
I never tried this, but it seems you can get the clients endpointId with Nearby.Connections.getLocalEndpointId() on the client devices. Not sure how this helps sending messages to other clients, because the host knows the Client-EndpointIds anyway...
However, as a proof of concept you could do something like this:
In the Host:
String payload = client2EndpointId;
Nearby.Connections.sendReliableMessage(mGoogleApiClient, client1EndpointId, payload);
In the Client1:
#Override
public void onMessageReceived(String endpointId, byte[] payload, boolean isReliable) {
String client2EndpointId = (String) payload;
Nearby.Connections.sendReliableMessage(mGoogleApiClient, client2EndpointId, messageFromClient1ToClient2);
}
And in Client2:
#Override
public void onMessageReceived(String endpointId, byte[] payload, boolean isReliable) {
String messageFromClient1 = (String) payload;
}
The Host sends client2's EndpointId as a message to client1. Client1 then uses this endpointId to send a message to client2.
Connections when using Nearby Connections API are lost when
you or other device gets disconnected to wifi
you and other device are connected to different wifi network (remember you are Nearby Connection which works on local network, if you want to send message via different network use Nearby Message API)
connection is lost even if the application in one of the device is minimised, closed(destroyed) etc
so I think you should log some of these cases.
"it may send messages to other client devices"
developer page mean that a connection status (Accepted, Rejected etc) can be send when the host responds to the connection request.
If multiple client devices are connected to a Host device, then how a
client device can send a message to other client devices?
And, if multiple devices are connected you can send your message (or I should rather say Data) to the host which will send the same data to all the clients connected to it (That's how most of the Host-Client system works).
I hope it helped

Presence listener not working in asmack

I am developing a chat application for Android using asmack. The connection is getting established and I'm able to login also. I've added listener for IQ packets and that is working fine. I have added listener for Presence packets also, here is my code:
PacketTypeFilter presenceFilter=new PacketTypeFilter(Presence.class);
connection.addPacketListener(new PacketListener() {
public void processPacket(Packet packet) {
Presence presence = (Presence)packet;
Log.d("test1","Presence packet came :From:"+presence.getFrom()+",Packet ID:"+presence.getPacketID()+",Priority:"+presence.getPriority()+",To:"+presence.getTo()
+",Exn:"+presence.toXML());
if (presence.getType() == Presence.Type.available) {
Log.d("test1","User available");
}
else if (presence.getType() == Presence.Type.unavailable) {
Log.d("test1","User not available");
}
}
}, presenceFilter);
My problem is, this listener is never getting called for any incoming Presence packets. It's working for outgoing ones.
I can't use Roster for handling presence as our server is using some Intelligent Routing Service for handling user availability.
Am I doing anything wrong here? Any input will be helpful.
If you can't use roster then are you sure that any presence is being sent to this client in the first place?
Is the user that the client is signed in with on the roster of other users on the network?
Have you tested explicitly sending a presence packet to the client using a client where you can edit the raw xml?

Android/XMPP: Unable to reconnect to Server after Connection Type Changes

I'm currently working on a Project in my University which is an Android-App which is supposed to deliver data to a Server.
In order to do that I require a more or less consistent connection to the server via XMPP. It is not really important that the connection is there 100% of the time, but it because the system is supposed to be more or less invisible to the user, user-interaction is supposed to be minimal.
Both the server and the client are xmpp-clients. I use jabber.org as the xmpp server.
I have an Android-Service that is establishing the connection to server and delivers data and this works fine.
Now I tried to make the Service reconnect when connection is lost or is Changed from Wifi to GSM. I wanted to try to make this work with a broadcastreceiver listening to NETWORK_STATE_CHANGED_ACTION. But I did not even get this far.
This is the problem: I tried running the app and then just disabling my wifi. My Phone than automatically switches to GSM and I lose my connection (which I anticipated). But when I try to reconnect manually (e.g. restarting the service) I get errors from the Server. Also my status is still "available". From that moment on it takes way too long until I can connect again.
06-29 18:12:14.888: WARN/System.err(14246): resource-constraint(500)
06-29 18:12:14.890: WARN/System.err(14246): at org.jivesoftware.smack.NonSASLAuthentication.authenticate(NonSASLAuthentication.java:110)
06-29 18:12:14.890: WARN/System.err(14246): at org.jivesoftware.smack.XMPPConnection.login(XMPPConnection.java:404)
06-29 18:12:14.890: WARN/System.err(14246): at org.jivesoftware.smack.XMPPConnection.login(XMPPConnection.java:349)
....
I am actually connected to the xmpp Server but it does not deliver my message:
06-29 18:12:14.882: INFO/System.out(14246): 06:12:14 nachm. RCV (1079704816): <iq type='error' id='7rhk4-70'><error code='500' type='wait'><resource-constraint xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></iq>
Sometimes I don't get the error, but still no messages are being delivered.
So I think that the server is not allowing me to connect, because I did not disconnect before trying to reconnect. Which I find strange because I thought you could even connect from multiple clients to one account.
I'm posting some code that I think might be relevant:
public void connectToServer() throws XMPPException {
ConnectionConfiguration config = new ConnectionConfiguration(
serverADD, port,
service);
connection = new XMPPConnection(config);
connection.connect();
SASLAuthentication.supportSASLMechanism("PLAIN", 0);
connection.login(user_JID,password);
Presence presence = new Presence(Presence.Type.available);
presence.setStatus("available");
presence.setPriority(24);
presence.setMode(Presence.Mode.available);
connection.sendPacket(presence);
}
this is how I send the messages:
public void sendMessage(String message, String recipient) throws XMPPException {
chat = connection.getChatManager().createChat(recipient, this);
chat.sendMessage(message);
}
Does anyone hava an idea how to solve this? I would even use "dirty" tricks as long as my message gets delivered to the server.
By the way: The senders and the recipients jids are always the same (after initial setup). Just in case anyone thinks that could matter.
Deadlock during Smack disconnect
As Airsource Ltd. mentioned in his comment: Smack is suffering from an deadlock in disconnect() which is loged as SMACK-278. I have made a commit that fixes this in my smack fork.
Android reconnection handling
For the network fallover, have a look at the GTalkSMS receiver. It will issue a ACTION_NETWORK_CHANGED intent, with the boolean extras "available" and "fallover". Your service should "stop" the connection when "available=false" and "fallover=false". How you "stop" the connection is up to you. Sometimes the disconnect() takes very long even with the fix for SMACK-278, this is why we do the disconnect in an thread that will abort after x seconds and then creates a new Connection instance. We reconnect then when the intent with "available=true" is received.
You will find other examples in the GTalkSMS source. I have the app permanently running and it achieves a stable, but not 100% available connection (because of WLAN <-> GSM switches).

Categories

Resources