smack connect to xmpp server with previous stream id - android

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 .

Related

OkHttp retry connection on another IP if 503

In my backend application, my load balancer uses round robin to distribute traffic. So, I have two A records against my hostname:
- x.x.x.x myapp.com
- y.y.y.y myapp.com
In any one of them is down it returns 503 Service Unavailable.
Now, in my Android application, I am using OkHttp 4.x to do network operations and the DNS resoulution is cached on the device. So, if the cahced IP goes down, it does not try to reach the other IP and the request fails. I tried multiple approaches to make it work but no cussess till now. The things I tried:
Add a Network interceptor and set Retry-After: 0 header to force it to retry the request.
Add a count variable and send the request again until count becomes 0
Set Connection Pool to .connectionPool(new ConnectionPool(0, 1, TimeUnit.MILLISECONDS)) to avoid caching the connection and retry the request
As per the docs, it says it should retry automatically on failed conenction, but it doesn't for status code 503. I verified this in the profiler and I could see only 1 request going.
To make sure both the IPs are resolved, I added the below code:
OkHttpClient client = new OkHttpClient.Builder().build();
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
try {
List<InetAddress> addr = client.dns().lookup("myapp.com");
for ( InetAddress a : addr ) {
Log.d(TAG, a.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
And I get both the entries in the log:
myapp.com/x.x.x.x
myapp.com/y.y.y.y
So, is there a way to make the OkHttp client try the other IP in case of failure?
This cannot be done by any app, unless you have access to your DNS server source to make him choose in which ip to send the request.
If the DNS records where editable by any means, like for example using a hosts file to determine to which IP a domain points, then you could change on the fly that file, and job done. but Android OS/IOS will not let you do that even if there was a hosts file.
You cannot choose to send the request to the a certain ip unless you know of a magic way to pass the domain name to that request in the headers and especially if it has an SSL on it.
The only way to fail over to one server, if the other is down, is if you place in your infrastructure a director which will know which of your two servers is up and running and routes the request to that one.

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).

How can i listen incoming subscription request in smack openfire android

I am creating an android chat application using a smack open fire. The problem that I am facing is I know how to send and accept subscription requests but how can I know if I have received any subscription request.
I have used Stanza listener but the listener is only listening to incoming chat messages, not subscription requests.
Below is my code
StanzaFilter filter=new StanzaTypeFilter(Message.class);
PacketCollector collector=connection.createPacketCollector(filter);
connection.addAsyncStanzaListener(new StanzaListener() {
#Override
public void processPacket(Stanza packet) throws SmackException.NotConnectedException {
Log.wtf("MA","Stanz listner");
}
},filter);
I am a beginner in smack, please help me how can I listen for incoming subscription request. Thanks in advance.
A subscription request is not a Message. It is a Presence instead.
So, you probably should try:
StanzaFilter filter = new StanzaTypeFilter(Presence.class);
Reference: RFC 3921 - Section 6 - Managing Subscriptions
This is how I have done in my android code
StanzaFilter subscribefilter = PresenceTypeFilter.SUBSCRIBE;
PresenceSubscribeListener subscribeListener = new PresenceSubscribeListener(context.getApplicationContext(), connection);
connection.addSyncStanzaListener(subscribeListener, subscribefilter);
And then this connection object has been referenced in a long running service in Android. This has been done such that when a packet is received when you app is in background, you can still process the incoming presence packet.
P.S. I am using smack 4.1.9 in my android code.

Socket.io client on Google Glass

Im trying to let my Google Glass and my android phone connect to a NodeJs server that Im running on my computer, so that I can send messages from my android phone to my Google Glass.
For this Im using koush's AndroidAsync library, which works great on my android phone and I have absolutely no trouble connecting my phone to the NodeJS server with this library.
However, the same code doesnt seem to work on my Google Glass. My Google Glass DOES connect, because the on connection eventhandler of my NodeJS server IS triggered, it just doesnt seem to trigger any of the ConnectCallback functions on my Google Glass.
Here is the code Im using in my Google Glass app:
SocketIOClient.connect(AsyncHttpClient.getDefaultInstance(), "http://192.168.1.229:5000", new ConnectCallback() {
#Override
public void onConnectCompleted(Exception ex, SocketIOClient client) {
Log.i("SOCKET", "CONNECTION COMPLETED");
if (ex != null) {
ex.printStackTrace();
return;
}
client.setStringCallback(new StringCallback() {
#Override
public void onString(String string, Acknowledge acknowledge) {
Log.d("SOCKET", string);
}
});
client.setJSONCallback(new JSONCallback() {
#Override
public void onJSON(JSONObject jsonObject, Acknowledge acknowledge) {
Log.d("SOCKET", jsonObject.toString());
}
});
client.on("event", new EventCallback() {
#Override
public void onEvent(JSONArray jsonArray, Acknowledge acknowledge) {
Log.i("DATA: ", jsonArray.toString());
Gson gson = new Gson();
}
});
mClient = client;
}
});
}
As you can see, Im trying to log "CONNECTION COMPLETED" in the "onConnectCompleted" function, but it never fires and nothing is ever logged.
I find this rather strange, as the same code DOES work on my android phone and "CONNECTION COMPLETED" IS logged when I run this bit of code on my android phone. The strangest thing is that my node server actually picks up the Google Glass as the on connection event is triggered on the server when my Glass connects.
So, can anybody help me find out why my Google Glass IS apparently connecting to my NodeJS server, but is NOT triggering any events when it connects. (Not triggering the ConnectCallback functions, "CONNECTION COMPLETED" is never logged)?
Thanks in advance,
Bram
I was facing the same issue here and noticed although my Glass showed it was connected to my Wifi network, it actually wasn't. I tried adb shell netcfg on it and to my surprise the wlan0 interface had no IP assigned. I reconnected it to the wifi network again and it all started to work just fine.
The weird thing though is that I was expecting some sort of error to be logged (even though I was using a different Socket.IO client). I believe the case to be a timeout that didn't expire (connection/socket timeout) so it was still attempting to connect and didn't fail in time for us to see the log entry. I guess these libraries have a relatively high connection timeout set by default, so you wouldn't see an error before many seconds would go by.

Queuing Messages When Internet Not Available in XMPP

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();
}
});

Categories

Resources