I have developed and Android application with is connecting to JAVA Web Socket and basically it is working very well.
The issue is, that sometimes the client is disconnected but connection at the server side is appearing to be connected.
I try to investigate, when and why it happened but unfortunately i could not find the a specific scenario that cause to this problem.
I have thought to implement Ping/Pong messaging between server and all clients and in case that there is no answer from the clients is to closed the connection at the server side.
i can easily implement such my private mechanism but I have read around and I understand that Java and Android has an build in Ping/Pong messaging mechanism but i was not able to find any example of that.
Can anyway, provide a simple example how to implement Ping/Pong messaging functionality using the build in tools?
I have succeed to implement Ping/Pong functionality between EE JAVA WebSocket and android application. The server is sending Ping message to client every 5 min. if server does not got Pong message back within 5 second, the server is closing client connection.
here is my solution if someone will need it:
WebSocket side:
public class User {
public ScheduledExecutorService pingExecutorService;
public Timer disconnectTimer;
private Session userSession;
private String userName;
public User(Session userSession) {
this.userSession = userSession;
}
public Session getUserSession() {
return userSession;
}
public void setUserSession(Session userSession) {
this.userSession = userSession;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
#OnOpen
public void onOpen(Session session) {
User newUserConnection = new User(session);
connections.getConnections().put(session.getId(), newUserConnection);
schedulePingMessages(newUserConnection);
}
#OnClose
public void onClose(Session session) {
handleOnClose(session);
}
#OnMessage
public void onMessage(String message, Session session) {
messageHandler.handleMessage(message, session);
}
#OnMessage
public void onPong(PongMessage pongMessage, Session session) {
String sourceSessionId = session.getId();
User user = connections.getConnections().get(sourceSessionId);
user.disconnectTimer.cancel();
user.disconnectTimer.purge();
}
#OnError
public void onError(Throwable t) {
System.out.println(new Date() + "onError::" + t.getMessage());
t.printStackTrace();
}
private void schedulePingMessages(User newUserConnection) {
newUserConnection.pingExecutorService = Executors.newScheduledThreadPool(1);
newUserConnection.pingExecutorService.scheduleAtFixedRate(() -> {
scheduleDiconnection(newUserConnection);
try {
String data = "Ping";
ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
newUserConnection.getUserSession().getBasicRemote().sendPing(payload);
} catch (IOException e) {
e.printStackTrace();
}
}, 300, 300, TimeUnit.SECONDS);
}
private void scheduleDiconnection(User user) {
user.disconnectTimer = new Timer();
user.disconnectTimer.schedule(new TimerTask() {
#Override
public void run() {
try {
user.getUserSession().close(new CloseReason(CloseCodes.UNEXPECTED_CONDITION," Client does not response"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, 5000);
}
The android side just need to add the following override method:
#Override
public void onWebsocketPing(WebSocket conn, Framedata f) {
Log.i("ZCF","got Ping !");
super.onWebsocketPing(conn, f);
}
I am trying to implement connection failover in android application. Basically I have list of URLs which application connect using websocket, In case of unable to connect with any url I need to try a second connection. API which I am using to connect with websocket in android side is not synchronous. so when I start connect to websocket after executing this method I dont know either connection establish or not until OnOpen method called and then I set some boolean there to use further.
What I tried: One solution in my mind is to start to call connect method from a thread and sleep this for certain time if OnOpen method called within timespan is ok otherwise try to use second connection url.
Can someone give me hint how I can resolve this. Here is a sample code (without thread implementation):
private DataUploader() {
// test list of url
urlList = new ArrayList<String>();
urlList.add("192.168.220.197:9001/observation/");
urlList.add("192.168.220.197:9002/observation/");
urlList.add("echo.websocket.org");
connectWebSocket();
}
public void connectWebSocket() {
if (urlList.size() > -1) {
String url = urlList.get(index);
this.connect(url);
index++;
}
}
private void connect(String websocketEndPointUrl) {
URI uri;
try {
websocketEndPointUrl = "ws://" + websocketEndPointUrl;
uri = new URI(websocketEndPointUrl);
} catch (URISyntaxException e) {
e.printStackTrace();
return;
}
mWebSocketClient = new WebSocketClient(uri) {
#Override
public void onOpen(ServerHandshake serverHandshake) {
Log.i("Websocket", "Opened");
isReady = true;
}
#Override
public void onMessage(String s) {
///
}
#Override
public void onClose(int i, String s, boolean b) {
Log.i("Websocket", "Closed " + s);
}
#Override
public void onError(Exception e) {
Log.i("Websocket", "Error " + e.getMessage());
}
};
mWebSocketClient.connect();
}
I'm having trouble getting my Android app to connect to a socket.io chat server. I'm using socket.io-java-client created by Gottox which can be found here: https://github.com/Gottox/socket.io-java-client
The server runs locally over port 7000. I'm using the android emulator, so I'm using 10.0.2.2:7000 to access the server.
Any help would be appreciated, I don't have much experience at all with SSL. If I find a working solution I'll also post it.
Node.js Server
var express = require('express');
var app = express();
var server = require('http').createServer(app).listen(7000);
var io = require('socket.io').listen(server);
io.sockets.on('connection', function(client){
client.on('message', function(err, msg){
client.broadcast.emit('message', msg);
});
});
package.json
{
"name": "simplechat",
"version": "0.0.1",
"main": "app.js",
"dependencies": {
"express" : "~4.0.0",
"socket.io" : "~0.9.13"
}
}
Android: SendMessageActivity
public class SendMessageActivity extends Activity {
private static final String SERVER_ADDRESS = "https://10.0.2.2:7000";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_send_message);
System.out.println("Sever: " + SERVER_ADDRESS);
try {
SocketIO socket = new SocketIO(new URL(SERVER_ADDRESS), new IOCallback() {
#Override
public void onDisconnect() {
System.out.println("disconnected");
}
#Override
public void onConnect() {
System.out.println("connected");
}
#Override
public void onMessage(String s, IOAcknowledge ioAcknowledge) {
}
#Override
public void onMessage(JSONObject jsonObject, IOAcknowledge ioAcknowledge) {
}
#Override
public void on(String event, IOAcknowledge ioAcknowledge, Object... objects) {
}
#Override
public void onError(SocketIOException e) {
e.printStackTrace();
}
});
} catch (MalformedURLException ex) {
ex.printStackTrace();
}
}
Android Permissions
<uses-permission
android:name="android.permission.INTERNET">
</uses-permission>
Error Code
08-09 16:07:28.224 8411-8441/com.example.puma.chatexample W/System.err﹕ io.socket.SocketIOException: Error while handshaking
08-09 16:07:28.225 8411-8441/com.example.puma.chatexample W/System.err﹕ at io.socket.IOConnection.handshake(IOConnection.java:322)
08-09 16:07:28.225 8411-8441/com.example.puma.chatexample W/System.err﹕ at io.socket.IOConnection.access$600(IOConnection.java:39)
08-09 16:07:28.225 8411-8441/com.example.puma.chatexample W/System.err﹕ at io.socket.IOConnection$ConnectThread.run(IOConnection.java:199)
08-09 16:07:28.226 8411-8441/com.example.puma.chatexample W/System.err﹕ Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'javax.net.ssl.SSLSocketFactory javax.net.ssl.SSLContext.getSocketFactory()' on a null object reference
08-09 16:07:28.226 8411-8441/com.example.puma.chatexample W/System.err﹕ at io.socket.IOConnection.handshake(IOConnection.java:302)
08-09 16:07:28.227 8411-8441/com.example.puma.chatexample W/System.err﹕ ... 2 more
I actually solved the problem. I used my PC's local IP http://192.168.0.xxx:7000 and the app was able to connect to the chat server from the emulator. I don't know why this works, but it might help out someone in the future :)
Update:
This is how I ended up structuring the project. I created a singleton class to handle socket connections Android side (you could also do it as a service). When receiving a message, the singleton class broadcasts an intent to the rest of the app. The intent is then picked up by a broadcast receiver in the relevant activity.
Android Side (singleton):
public class SocketSingleton {
private static SocketSingleton instance;
private static final String SERVER_ADDRESS = "http://1.2.3.4:1234";
private SocketIO socket;
private Context context;
public static SocketSingleton get(Context context){
if(instance == null){
instance = getSync(context);
}
instance.context = context;
return instance;
}
public static synchronized SocketSingleton getSync(Context context){
if (instance == null) {
instance = new SocketSingleton(context);
}
return instance;
}
public SocketIO getSocket(){
return this.socket;
}
private SocketSingleton(Context context){
this.context = context;
this.socket = getChatServerSocket();
this.friends = new ArrayList<Friend>();
}
private SocketIO getChatServerSocket(){
try {
SocketIO socket = new SocketIO(new URL(SERVER_ADDRESS), new IOCallback() {
#Override
public void onDisconnect() {
System.out.println("disconnected");
}
#Override
public void onConnect() {
System.out.println("connected");
}
#Override
public void on(String event, IOAcknowledge ioAcknowledge, Object... objects) {
if (event.equals("chatMessage")) {
JSONObject json = (JSONObject) objects[0];
ChatMessage chatMessage = new ChatMessage(json);
Intent intent = new Intent();
intent.setAction("newChatMessage");
intent.putExtra("chatMessage", chatMessage);
context.sendBroadcast(intent);
}
}
#Override
public void onError(SocketIOException e) {
e.printStackTrace();
}
});
return socket;
} catch (MalformedURLException ex) {
ex.printStackTrace();
}
return null;
}
}
Android Side (activity):
public class ChatActivity extends Activity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
IntentFilter newChatMessageFilter = new IntentFilter("newChatMessage");
this.registerReceiver(new MessageReceiver(), newChatMessageFilter);
...
public class MessageReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent){
final ChatMessage chatMessage =(ChatMessage) intent.getExtras().get("chatMessage");
runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.add(chatMessage);
mAdapter.notifyDataSetChanged();
}
});
}
}
}
Server Side:
var express = require('express');
var app = express();
var server = require('http').createServer(app).listen(1234);
var io = require('socket.io').listen(server);
io.sockets.on('connection', function(client){
console.log("client connected: " + client.id);
client.on("sendTo", function(chatMessage){
console.log("Message From: " + chatMessage.fromName);
console.log("Message To: " + chatMessage.toName);
io.sockets.socket(chatMessage.toClientID).emit("chatMessage", {"fromName" : chatMessage.fromName,
"toName" : chatMessage.toName,
"toClientID" : chatMessage.toClientID,
"msg" : chatMessage.msg});
});
});
I know this not really answers to the OP's posts, but for those who may be interested, this is a tutorial I made to make communicate your Android with a Node.js server -without any additional library- :
https://causeyourestuck.io/2016/04/27/node-js-android-tcpip/
This is a foretaste of how it looks like at the end:
Client socket = new Client("192.168.0.8", 1234);
socket.setOnEventOccurred(new Client.OnEventOccurred() {
#Override
public void onMessage(String message) {
}
#Override
public void onConnected(Socket socket) {
socket.send("Hello World!");
socket.disconnect();
}
#Override
public void onDisconnected(Socket socket, String message) {
}
});
socket.connect();
Puma has already answered on how you can implement a socket connection using SocketIO. This has nothing new to contribute. Yet, it is an attempt to help fellow newbies, as also introduce the implementation of Socket.io's java library.
Socket.IO has its own java implementation on Github, which you can follow along to create a socket application for Android/Java.
Android side:
Include this in your build gradle
compile ('io.socket:socket.io-client:0.8.3') {
// excluding org.json which is provided by Android
exclude group: 'org.json', module: 'json'
}
Provide Permission in your app:
<uses-permission android:name="android.permission.INTERNET" />
Android Code:
The structure of code is similar to how you would code in Node. The message in socket.on is similar to node's socket.on('message', ...)
import io.socket.client.Socket;
import io.socket.client.IO;
import io.socket.emitter.Emitter;
final Socket socket;
try{
socket = IO.socket("http://192.168.1.1:8080");
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
socket.emit("message", "hi");
socket.disconnect();
}
}).on("message", new Emitter.Listener() {
//message is the keyword for communication exchanges
#Override
public void call(Object... args) {
socket.emit("message", "hi");
}
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {}
});
socket.connect();
}
catch(Exception e){
}
Node.js side
Create normal sockets using socket.io
You're emulator network is different from your PC's as I have heard. So if you could by change try this on an actual phone that is connected to the same network as your PC.
You probably wont be able to ping 10.0.2.2 from your emulator or the other way around from your pc to emulator.
I'm trying learn how to use the websocket and make a simple servlet for being connected with Android but I don't get it.
The index.jsp :
var ws = new WebSocket("ws://" + document.location.host + "/myws/ServletWS");
ws.onopen = function() { };
ws.onclose = function() { };
ws.onerror = function() { log("ERROR"); };
ws.onmessage = function(data) { var message = data.data; };
function sendMessage(msg) { ws.send(msg); }
How or where I receive the data from client?
Now on the servlet:
#Override protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request) {
return new ConnectionWS();
}
class ConnectionWS extends MessageInbound {
private WsOutbound outbound;
#Override protected void onOpen(WsOutbound outbound) {
this.outbound = outbound;
}
#Override protected void onTextMessage(CharBuffer msg) throws IOException {
String message = msg.toString();
ServletWS.processData(message);
}
public void sendMessage(String message) {
CharBuffer cb = CharBuffer.wrap(message);
try {
outbound.writeTextMessage(cb);
} catch (IOException e) {}
}
}
public void processData(String message){
here I have to call the sendMessage with the answer to the client
}
I have saw a lot of examples on web but all of then about chat.
Thanks a lot for any help.
I understand that, you have a basic knowledge about tomcat configuration as well as java Servlet programming. As WekSocket is newly introduced in Tomcat, you may need to use latest tomcat version to implement WebSocket over it. I have used Apache Tomcat 7.0.42 for it.
So here we go. First, create a Servlet which will just create a new WebSocket for the request. You may need to modify it, if you want to go by session rather than request. Here is sample code.
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
public class WsChatServlet extends WebSocketServlet {
private static final long serialVersionUID = 1456546233L;
#Override
protected StreamInbound createWebSocketInbound(String protocol,
HttpServletRequest request) {
return new IncomingMessageHandler();
}
}
Now, create a Message Handler class which will handle each WebSocket stream independently. and that's it !
public class IncomingMessageHandler extends MessageInbound {
private WsOutbound myoutbound;
public IncomingMessageHandler() {
}
#Override
public void onOpen(WsOutbound outbound) {
logger.info("Open Client.");
this.myoutbound = outbound;
}
#Override
public void onClose(int status) {
logger.info("Close Client.");
}
/**
* Called when received plain Text Message
*/
#Override
public void onTextMessage(CharBuffer cb) throws IOException {
}
/**
* We can use this method to pass image binary data, eventually !
*/
#Override
public void onBinaryMessage(ByteBuffer bb) throws IOException {
}
public synchronized void sendTextMessage(String message) {
try {
CharBuffer buffer = CharBuffer.wrap(message);
this.getMyoutbound().writeTextMessage(buffer);
this.getMyoutbound().flush();
} catch (IOException e) {
}
}
/**
* Set websocket connection timeout in milliseconds,
* -1 means never
*/
#Override
public int getReadTimeout() {
return -1;
}
public WsOutbound getMyoutbound() {
return myoutbound;
}
public void setMyoutbound(WsOutbound myoutbound) {
this.myoutbound = myoutbound;
}
}
If not misunderstood and you want to use web sockets on Android then recommended API for you is jWebSocket.
Get it here, hopefully it already provides you APIs for a lot of the work that you need to do or even more.
http://jwebsocket.org/
I am trying to connect to a chat server on the internet from an Android app so first I test whether I have contact with the server from an AsyncTask:
#Override
protected Void doInBackground(SocketAndEditText... soEd) {
try{
InetAddress address1 = InetAddress.getByName("130.237.161.23");
boolean reachable = address1.isReachable(4456);
//...
} catch (Exception e) {
//...
}
}
When I run the above code the variable reachable gets the value false but when I run the same code as a simple Java console application I get true:
import java.net.InetAddress;
public class Test {
public static void main(String[] args) {
try{
InetAddress address1 = InetAddress.getByName("130.237.161.23");
boolean reachable = address1.isReachable(4456);
System.out.println(reachable);
} catch (Exception e) {
//...
}
}
}
In my manifest file I have <uses-permission android:name="android.permission.INTERNET" />
Why does it not work in Android but works as a Java application?