socket.io + android - disconnect socket and reconnect with different url - android

What I have:
I have Android application with socket.io implementation. Servers run on node.js. Servers IPs are hardcoded in the app. Clients communicate with servers successfully - they send and receive messages.
IO.socket version I am using in my client: ('io.socket:socket.io-client:1.0.0')
What I need to get: My problem is I have to change server IP which the client is connected to, after client receives determined message.
Simplified client socket behaviour is more less like this:
socket.on("msg", new Emitter.Listener() {
#Override
public void call(Object... args) {
try {
switch (msg){
case "1":
keepOnListeningFirstServer();
break;
case "2":
connectToAnotherServer();
break;
...
What I managed to achieve is that after specific server command (msg = 2) client connects to the second server, but client remains connected to the first server (client is still listening to commands from the first server). Actually what happens I think is that client creates second socket, but not properly closing the first socket.
In this situation, client still receives messages form first server but it sends responses to the new second server. Quite weird, but that was the furthest I managed to get with this here.
What I have tried to disconnect client and start new listener was calling on client:
socket.disconnect();
socket.close();
socket.off();
and then creating new socket and connecting it:
socket = IOSocket.getInstance(socketIP).getSocket();
socket.connect();
So my questions in general are:
Why isn't my client starting listening to new server messages even though this client is sending responses to this new server?
Maybe there is a way to switch/update socket listener without closing it?
What is a proper way of closing/disconnecting/destroying this first socket?
Maybe somebody can give me a hint of better method to change io.socket server IP that client is listening to? (Please note that this application is Android native)
Any help or advice will be very appreciated, so thanks in advance!

You should close the socket and shutdown the channel before the disconnection really completes.
This should be your case:
//create the initial socket
Socket socket = IO.socket("your_url");
//define a constant for your channel
public static final String ON_MESSAGE_RECEIVED = "msg";
//define the event listener
private Emitter.Listener onMessageReceived = new Emitter.Listener() {
#Override
public void call(Object... args) {
//your logic for the event here
}
});
//attach your channels to the socket
socket.on(ON_MESSAGE_RECEIVED, onMessageReceived);
socket.connect();
//when you want to disconnect the socket remember to shutdown all your channels
socket.disconnect();
socket.off(ON_MESSAGE_RECEIVED, onMessageReceived);
//Then create a new socket with a new url and register again for the same event
IO.Options opts = new IO.Options();
opts.forceNew = true;
opts.reconnection = false;
socket = IO.socket("new_url", opts);
socket.on(ON_MESSAGE_RECEIVED, onMessageReceived);
socket.connect();

Related

android client Not able to authorize signalR request using bearer-token

I am trying to connect signalR from the android client. I have already setup signalR hub and its working properly with javascript client on the browser. javascript client able to sent bearer-token and on the server side, I am able to get user identity.
But android java client is not able to send bearer token on. I am using https://github.com/SignalR/java-client library (As I am not using SIgnalR-core so not using latest SIgnalR core library)
connection = new HubConnection(serverUrl);
connection.getHeaders().put("Authorization","Bearer XYZ");
proxy = connection.createHubProxy(hubName);
When I run this code, I got an error
java.lang.InterruptedException: Operation was canceled
But when I don't send AUthorization header with the request then on server-side SIgnalR OnConnected() method called successfully.
The issue seems to be with sending Authorization header with the request.
For reference, the following is code to show how token authentication is implemented on the server-side
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new QueryStringOAuthBearerProvider()
});
var hubConfiguration = new HubConfiguration
{
Resolver = GlobalHost.DependencyResolver,
};
map.RunSignalR(hubConfiguration);
});
ConfigureAuth(app);
I have tried calling it by removing authorization from the server. Then it called successfully. But not works when called with Authorization header.
When I tried connection without Authorization then on server-side OnCOnnected method called but Context. Identity is null.
android Java code for connecting to SignalR client
Platform.loadPlatformComponent(new AndroidPlatformComponent());
// Create Connection
connection = new HubConnection(serverUrl);
connection.getHeaders().put("Authorization","Bearer XYZ");
// Create Proxy
proxy = connection.createHubProxy(hubName);
// Establish Connection
ClientTransport clientTransport = new
ServerSentEventsTransport(connection.getLogger());
SignalRFuture<Void> signalRFuture = connection.start(clientTransport);
try {
signalRFuture.get();
} catch (InterruptedException e) {
return false;
} catch (ExecutionException e) {
return false;
}
return true;
If you are using Websocket, try this
https://github.com/doctorcareanywhere/java-client
build signalr-client-sdk and import the jar to your project
eg.
implementation files('libs/signalr-client-sdk.jar')

How to perform "RXJava 2 queue jobs" until value changes?

I'm using RXJava2 in my Android app, and I have a somewhat peculiar scenario.
I want to perform unknown amount of jobs (determined by the user), but I want them to start only after a certain value has changed.
My specific requirement is to use a Socket for server communication,
and the flow is the following:
User requests some data - data is retrieved by sending data to the socket and wait for the response.
The module that communicates with the server should open a Socket connection, and only after the connection established, it may fetch the requested data.
While Socket attempt to connect, the user may request some more data.
After the connection established successfully the module should perform all the requests sent by the user while connection process was in progress.
The module also should publish the results that came for each data sent to the socket.
How can this be accomplished using RXJava2?
You could use an UnicastSubject for the queue part and do some flatMap-ping once the connection is established:
UnicastSubject<String> userRequests = new UnicastSubject.create();
Single.fromCallable(() -> new Socket("server", port))
.subscribeOn(Schedulers.io())
.flatMapObservable(socket -> {
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
byte[] responseBuffer = new byte[4096];
return userRequests
.observeOn(Schedulers.io())
.map(request -> {
out.write(request.getBytes());
int n = in.read(responseBuffer);
if (n >= 0) {
return new String(responseBuffer, 0, n);
}
throw new IOException("Socket closed while waiting for response");
})
.doFinally(() -> socket.close());
});
Since you are working on the Socket level, it is your responsibility to work out the proper encoding of the requests to be written and the proper decoding of the response to be read (i.e., how long (in bytes) the response is to a particular request).
I believe you would need FlowableTransformers.valve() for this, from RxJava2Extensions.
It should work something like this
PublishSubject<String> jobs = PublishSubject.create().toSerialized();
BehaviorSubject<Boolean> isConnected = BehaviorSubject.createDefault(false);
CompositeDisposable disposables = new CompositeDisposable();
public void connect() {
disposables.add(socketService.subscribe((success) -> {
isConnected = true;
}));
}
public void addJob(String job) {
jobs.onNext(job);
}
public void executeQueuedTasks() {
disposables.add(jobs
.toFlowable(BackpressureStrategy.BUFFER)
.compose(FlowableTransformers.valve(isConnected))
.subscribeWith(new DisposableObserver<>() {
...
})
);
}
public void destroy() {
disposables.clear();
}
}
But the UnicastSubject sample above is more likely to work, I wrote this off the top of my head.

Socket.io: Error: Callbacks are not supported when broadcasting at Socket.emit

I successed to send ack to android client from nodejs server but I don't succeed to do reverse. I have this error: Callbacks are not supported when broadcasting at Socket.emit
Serveur nodejs:
socket.broadcast.to(socketid).emit('message', data, callThis);
//this function is executed when client calls it
function callThis (dataFromClient){
console.log("Call back fired: " + dataFromClient);
}
client android:
socket.on("message", new Emitter.Listener() {
#Override
public void call(Object... args) {
Ack ack = (Ack) args[args.length - 1];
ack.call();
JSONObject data = (JSONObject) args[0];
.....
}
}
What can I do to resolve this problem?
Basically support the answer of #Xeoncross. When a connection came, just saved the socket into a map, like below
this.connections = new Map<string, SocketIO.Socket>()
this.server.on("connection", (socket: SocketIO.Socket) => {
this.connections.set(socket.id, socket)
})
Then use a loop to send all users individually
public broadcast(msg: string) {
for(const socket of this.connections.values()) {
socket.emit("block", msg, (confirm: string) => {
console.log("confirmation msg: ", confirm)
})
}
}
As the error says, "Callbacks are not supported when broadcasting". It doesn't look like you are broadcasting though, as you are trying to send to a single client. So assuming socket is an actual client socket instance you can change your code:
socket.broadcast.to(socketid).emit('message', data, callThis);
to just send to that one person
socket.emit('message', data, callThis);

Incorrect encoding exception when connecting to FCM using Smack Library

I am trying to connect to FCM using the smack library:
Here's what I've tried. It works, but I get an exception when the connection tries to login.
new Thread(new Runnable(){
XMPPTCPConnectionConfiguration.Builder configBuilder = XMPPTCPConnectionConfiguration.builder();
private Handler umm;
#Override
public void run() {
configBuilder.setSecurityMode(XMPPTCPConnectionConfiguration.SecurityMode.disabled );
configBuilder.setServiceName("fcm-xmpp.googleapis.com");
configBuilder.setHost("fcm-xmpp.googleapis.com");//MAYBE PROBLEM HERE??
configBuilder.setPort(5236);
configBuilder.setCompressionEnabled(false);
configBuilder.setSendPresence(true);
configBuilder.setSocketFactory(SSLSocketFactory.getDefault());
InterfaceClass.FCMconnection = new XMPPTCPConnection(configBuilder.build());
umm = yes;
try {
InterfaceClass.FCMconnection.connect();
Log.v("pony", "white horse");
//InterfaceClass.FCMloggin.start();
android.os.Message y4 = android.os.Message.obtain();
y4.what = LOGINTOFCM;
umm.sendMessage(y4);
//the rest of the thread is just exception handling in catch clauses
Once my handler receives the message I attempt to login with the connection
like this:
try { FCMconnection.login("senderId#gcm.googleapis.com","SERVER_KEY");
Log.d("black","r2d2");
} catch (XMPPException e) {//exception thrown here
e.printStackTrace();
Log.d("black","maity "+e);
I get the following excecption:
"smack.sasl.SASLErrorException: SASLError using X-OAUTH2: incorrect- encoding"
Now from the documentation it says clearly to implement SASL plain mechanism,
but I don't know how? Here's what the documentation says:
"The connection has two important requirements:
You must initiate a Transport Layer Security (TLS) connection. Note that CCS doesn't currently support the STARTTLS extension.
CCS requires a SASL PLAIN authentication mechanism using #gcm.googleapis.com (FCM sender ID) and the Server key as the password, where the sender ID and Server key are the values you gathered when configuring your client app. See the client documentation for your platform for information on obtaining these credentials."
Does anybody have any idea what could be causing this exception? How should I connect to FCM with the smack library?
Thank you for any advice.
As per documentation connecting to FCM over XMPP protocol needs:
1) TLS connection in transport layer, to achieve this create SSLContext using TLS protocol extension
2) Plain SASL protocol, make sure "smack-sasl-javax-4.1.8.jar" is integrated into your build setup. This took me lot of time to figure out
3) Host, service name and port number are correct(refer code snippet below)
Below code snippet works perfectly for me:
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, null);
} catch (Exception e) {
//Failed to get default ssl context with TLS enabled... something can't proceed further
}
XMPPTCPConnectionConfiguration.Builder config = XMPPTCPConnectionConfiguration.builder();
config.setConnectTimeout(CONNECTION_TIMEOUT);
config.setSendPresence(true);
config.setCustomSSLContext(sslContext);
config.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
config.setServiceName("gcm.googleapis.com");
config.setHost("fcm-xmpp.googleapis.com");
config.setPort(5236);//not production server
config.setDebuggerEnabled(true);
config.setCompressionEnabled(true);
config.setSocketFactory(sslContext.getSocketFactory());
(mConnection = new XMPPTCPConnection(config.build())).addConnectionListener(ConnectionSession.this);
mConnection.setPacketReplyTimeout(REPLY_TIMEOUT);
mConnection.connect();
mConnection.login(userID, password); //use your app server credential here
Smack version 4.8.1 implemented and tested from openfire setup.
Hope this help someone!!

How to attach POST variables to socket.io connection?

I have a webchat application which is running on Node.js and Socket.io. After user logins on main homepage port 80, he gets redirected to port 3000 (chat application) along with POST data containing his ID, username etc... On chat application page it validates details and registers user as new client.
However, now I am building android chat application and I get immediately disconnected from chat because basically android client fails validation since there is no POST data attached.
How can I add POST data along with connection request?
Here is a code that does it all in android:
try {
IO.Options opts = new IO.Options();
opts.forceNew = true;
final Socket socket = IO.socket("http://host:3000", opts);
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener(){
#Override
public void call(Object... args){
socket.emit("mobile_ping", sf.getTextValue(MainActivity.this, R.id.editText));
}
});
socket.connect();
} catch (Exception e) {
Log.d("ERR", String.valueOf(e));
}

Categories

Resources