Closed fracturedexistence closed 4 years ago
The first problem I can see is that you are creating SocketIoServer
objects inside the handler. It should be moved to the same place as EngineIoServer
object creation.
Also, you have not setup websocket handling, at least in the code that you have posted. Please note that the websocket handler and the HTTP handler both need the same object of EngineIoServer
and SocketIoServer
to function properly.
I would highly suggest enabling async support in your servlet as without it, the server is flooded by HTTP requests and the way Java Servlets are implemented, there's no way for EngineIoServer to enable it on a per request basis.
SocketIoServer
class is somewhat memory intensive and creating a new object for every request both strains the memory and messes up the state of EngineIoServer
. It also effects the client ID generation as you can see from the logs.
Other than that, can you also post the browser network log and check if there are 400 error codes?
Hi Thanks for replying.
Yes, I did mention that I tried following the instructions verbatum in your documentation, but I had changed it because it was defective. Ie. was way worse!
So I've changed it back to the original, and added listeners for engineioserver, but as I said, the results are worse. The site dies within seconds because now it's overrun with requests from one single browser.
There are no more files. This is the only file I've implemented because I was of the impression (from your docs) that it was a straight portal of (Node.js) socket.io
package servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
import io.socket.emitter.Emitter; import io.socket.engineio.server.EngineIoServer; import io.socket.engineio.server.EngineIoSocket; import io.socket.socketio.server.SocketIoNamespace; import io.socket.socketio.server.SocketIoServer; import io.socket.socketio.server.SocketIoSocket;
@WebServlet("/socket.io/*") public class SocketIoServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
private final EngineIoServer mEngineIoServer = new EngineIoServer();
private final SocketIoServer mSocketIoServer = new SocketIoServer(mEngineIoServer);
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[WEBSOCKET] SOCKET.IO: doGet");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[WEBSOCKET] SOCKET.IO: doPost");
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
mEngineIoServer.handleRequest(request, response);
mEngineIoServer.on("connection", new Emitter.Listener() {
@Override
public void call(Object... args) {
EngineIoSocket socket = (EngineIoSocket) args[0];
System.out.println("[ENGINE.IO] Connection. Args=" + args.length+" Socket id=" + socket.getId());
socket.on("message", new Emitter.Listener() {
@Override
public void call(Object... args) {
try {
System.out.println("[ENGINE.IO] Message. Args=" + args.length);
System.out.println(args[0]);
} catch (Exception e) {
System.out.println("Error");
}
}
});
socket.on("notify", new Emitter.Listener() {
@Override
public void call(Object... args) {
try {
System.out.println("[ENGINE.IO] Message. Args=" + args.length);
System.out.println(args[0]);
} catch (Exception e) {
System.out.println("Error");
}
}
});
socket.on("ping", new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("[ENGINE.IO] Ping");
}
});
socket.on("pong", new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("[ENGINE.IO] Pong");
}
});
socket.on("error", new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("[ENGINE.IO] Error");
}
});
}
});
SocketIoNamespace namespace = mSocketIoServer.namespace("/");
namespace.on("connection", new Emitter.Listener() {
@Override
public void call(Object... args) {
SocketIoSocket socket = (SocketIoSocket) args[0];
System.out.println("[SOCKET.IO] Connection. Args=" + args.length+" Socket id=" + socket.getId());
socket.on("message", new Emitter.Listener() {
@Override
public void call(Object... args) {
try {
System.out.println("[SOCKET.IO] Message. Args=" + args.length);
System.out.println(args[0]);
} catch (Exception e) {
System.out.println("Error");
}
}
});
socket.on("notify", new Emitter.Listener() {
@Override
public void call(Object... args) {
try {
System.out.println("[SOCKET.IO] Message. Args=" + args.length);
System.out.println(args[0]);
} catch (Exception e) {
System.out.println("Error");
}
}
});
socket.on("ping", new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("[SOCKET.IO] Ping");
}
});
socket.on("pong", new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("[SOCKET.IO] Pong");
}
});
socket.on("error", new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("[SOCKET.IO] Error");
}
});
}
});
}
public void init() throws ServletException {
System.out.println("[WEBSOCKET] SOCKET.IO: Init");
}
public void destroy() {
System.out.println("[WEBSOCKET] SOCKET.IO: DESTROYED");
}
}
This time it just goes to town with repeated requests on engine.io instead [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf [ENGINE.IO] Connection. Args=1 Socket id=N9lWJaf ...
RE: "you have not setup websocket handling"
Now I'm really confused, isn't that what socketioserver and engineioserver are? Handlers for socket.io and http connections?
I'm starting to tear my hair out over this. Do I have to set up another websocket handler? is it EngineIoEndpoint? or EngineIoHandler? that you mentoned in the appendix part of your docs? And how do they marry up with my servlet? Sorry, but I'm just confused, If you could elaborate more, it would be helpful
Mel
Socket.io supports and prefers websocket but falls back to HTTP if it's not available. It's a bit more tedious to setup websocket in Java but you can follow this to setup websocket support. Also, enable async support on your servlet for better performance.
Move the namespace.on("connection",...
event listener outside of your service
method and into your constructor. Currently, it's being called on every request and putting a lot of stress on your memory and CPU.
I'd suggest moving your EngineIoServer
and SocketIoServer
objects into a static context so you can access them from both the HTTP and WebSocket handlers.
Don't use mEngineIoServer.on(...)
to register any event listeners, it'll interfere with SocketIoServer
operations. Only use mSocketIoServer.on(...)
.
Hi
Ah... Something I missed in your documents were the separation of the 2 servlets So I've now created SocketIoServlet and EngineIoServlet object classes
@WebServlet(value = "/engine.io/*", asyncSupported = true) public class EngineIoServlet extends HttpServlet
@WebServlet(value = "/socket.io/*", asyncSupported = true) public class SocketIoServlet extends HttpServlet
Now... "moving your EngineIoServer and SocketIoServer objects into a static context so you can access them from both the HTTP and WebSocket handlers" is a little abstract. I have a GlobalServlet that handles our shared pools, I guess I could add these declarations in there, and that should satisfy that criteria... (I assume) public static EngineIoServer mEngineIoServer = new EngineIoServer(); public static SocketIoServer mSocketIoServer = new SocketIoServer(mEngineIoServer);
So I've done that now.
This has instantly reduced the number of calls down to a few dozen rather than a zillion (which is promising). Although, shortly after processing the first notifications, it seems to disconnect and refuse all reconnection attempts. 0:=io.socket.engineio.client.EngineIOException: xhr poll error
RE:Don't use mEngineIoServer.on(...) Ok, understood, despite your documentation saying "Attach a listener to the connection event of EngineIoServer to listen for new connections.".
RE:Only use mSocketIoServer.on(...) There is no "mSocketIoServer.on(...)" method, The only way I could see it being achieved is by using "namespace.on(...)"
I'll post the revised code shortly, just to be sure that I'm doing it right
I won't post my GlobalServlet file because it contains a lot of business sensitive info. But the following static objects are declared in there
public static EngineIoServer wsEngineIoServer = new EngineIoServer(); public static SocketIoServer wsSocketIoServer = new SocketIoServer(wsEngineIoServer);
SocketIoServlet.java package servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
import servlet.GlobalServlet;
import io.socket.emitter.Emitter; import io.socket.engineio.server.EngineIoServer; import io.socket.socketio.server.SocketIoNamespace; import io.socket.socketio.server.SocketIoServer; import io.socket.socketio.server.SocketIoSocket;
@WebServlet(value = "/socket.io/*", asyncSupported = true) public class SocketIoServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public SocketIoServlet()
{
EngineIoServer mEngineIoServer = GlobalServlet.wsEngineIoServer;
SocketIoServer mSocketIoServer = GlobalServlet.wsSocketIoServer;
SocketIoNamespace namespace = mSocketIoServer.namespace("/");
namespace.on("connection", new Emitter.Listener() {
@Override
public void call(Object... args) {
SocketIoSocket socket = (SocketIoSocket) args[0];
System.out.println("[SOCKET.IO] Connection. Args=" + args.length+" Socket id=" + socket.getId());
socket.on("message", new Emitter.Listener() {
@Override
public void call(Object... args) {
try {
System.out.println("[SOCKET.IO] Message. Args=" + args.length);
System.out.println(args[0]);
} catch (Exception e) {
System.out.println("Error");
}
}
});
socket.on("notify", new Emitter.Listener() {
@Override
public void call(Object... args) {
try {
System.out.println("[SOCKET.IO] Message. Args=" + args.length);
System.out.println(args[0]);
} catch (Exception e) {
System.out.println("Error");
}
}
});
socket.on("ping", new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("[SOCKET.IO] Ping");
}
});
socket.on("pong", new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("[SOCKET.IO] Pong");
}
});
socket.on("error", new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("[SOCKET.IO] Error");
}
});
}
});
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
EngineIoServer mEngineIoServer = GlobalServlet.wsEngineIoServer;
SocketIoServer mSocketIoServer = GlobalServlet.wsSocketIoServer;
System.out.println("[SOCKET.IO] SERVICE CALL");
mEngineIoServer.handleRequest(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[SOCKET.IO] SOCKET.IO: doGet");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[SOCKET.IO] SOCKET.IO: doPost");
}
public void init() throws ServletException {
System.out.println("[WEBSOCKET] SOCKET.IO: Init");
}
public void destroy() {
System.out.println("[WEBSOCKET] SOCKET.IO: DESTROYED");
}
}
EngineIoServlet.java @WebServlet(value = "/engine.io/*", asyncSupported = true) public class EngineIoServlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
// private final static EngineIoServer mEngineIoServer = new EngineIoServer();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[WEBSOCKET] SOCKET.IO: doGet");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[WEBSOCKET] SOCKET.IO: doPost");
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
EngineIoServer mEngineIoServer = GlobalServlet.wsEngineIoServer;
System.out.println("[ENGINE.IO] SERVICE CALL");
mEngineIoServer.handleRequest(request, response);
}
public void init() throws ServletException {
System.out.println("[WEBSOCKET] SOCKET.IO: Init");
}
public void destroy() {
System.out.println("[WEBSOCKET] SOCKET.IO: DESTROYED");
}
}
Logs...
Server sends a message successfully.
[SOCKET.IO] SERVICE CALL [SOCKET.IO] Message. Args=1 Received message: {"command":"initialise","system_name":"RESIN","message":"Hello"}
But then ... 02-06-20 14:34:01 WEBSOCKET ERROR 0:=io.socket.engineio.client.EngineIOException: xhr poll error
02-06-20 14:34:01 WEBSOCKET DISCONNECT 0:=transport error
[SOCKET.IO] SERVICE CALL
unable to connect to port.
[SOCKET.IO] SERVICE CALL
[SOCKET.IO] SERVICE CALL
[SOCKET.IO] Connection. Args=1 Socket id=N9ouC12
02-06-20 14:34:02 WEBSOCKET RECONNECT
0:=1
[SOCKET.IO] SERVICE CALL [SOCKET.IO] SERVICE CALL 02-06-20 14:34:02 WEBSOCKET CONNECT
[SOCKET.IO] SERVICE CALL [SOCKET.IO] SERVICE CALL [SOCKET.IO] SERVICE CALL [SOCKET.IO] Connection. Args=1 Socket id=N9ouC13 [SOCKET.IO] SERVICE CALL [SOCKET.IO] SERVICE CALL [SOCKET.IO] SERVICE CALL 02-06-20 14:34:12 WEBSOCKET ERROR 0:=io.socket.engineio.client.EngineIOException: xhr poll error
02-06-20 14:34:12 WEBSOCKET DISCONNECT 0:=transport error
Remove the EngineIoServlet
class as it's not needed.
The engine.io documentation will ask you to use mEngineIoServlet.on(...)
because that's the defined API. SocketIoServer
extends EngineIoServer
and handles all connections and raw messages. Attaching event listeners on EngineIoServer
messes with the control/data flow possibly causing random errors.
Yes, I meant namespace.on(...)
instead of mSocketIoServer.on(...)
.
Can you also post your websocket handler code?
I'm still unsure of how to implement the websocket handler, currently it has fallen back on polling.
How and where do I implement EngineIoEndpoint? I stored it in our src folder but Resin doesn't know what to do with it, so it just sits there doing nothing
I'm not familiar with Resin but if it follows the specification properly, you can add the websocket handler by adding the EngineIoEndpoint
class and the ApplicationServerConfig
class and it should work.
public final class ApplicationServerConfig implements ServerApplicationConfig {
@Override
public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> endpointClasses) {
final HashSet<ServerEndpointConfig> result = new HashSet<>();
result.add(ServerEndpointConfig.Builder
.create(EngineIoEndpoint.class, "/socket.io/")
.build());
return result;
}
@Override
public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
return null;
}
}
Thanks. I've removed EngineIoServlet. Yeah, I saved EngineIoEndpoint and ApplicationServerConfig to src, and nothing happened. Zilch
RE: websocket handler code? Do you mean the socketio-client java code?
So it looks like the websocket implementation for Resin is a bit weird.
I found some documentation about getting websocket to work here.
By the looks of it, you'll need to modify SocketIoServlet.service(..)
to detect if transport
query parameter is set to websocket
and then inject the WebSocketListener
. BTW, you'll also need to write a class that implements WebSocketListener
that passes data to EngineIoServer
by emiting the message
event.
If you can get it working, can you please post some of the code so I can include it in the documentation?
Oh.. yeah those pages are terribly outdated, the examples are defective, and they show no interest in helping their customers to get websockets working. I had words to them about that. Grr.
But i'll read through it and see if I can jig up something that works. And yes, absolutely, I'd be happy to share my code
Just a question though...
In your documentation, you write... "Handling WebSocket connections involves creating an instance of EngineIoWebSocket and passing it in a call to handleWebSocket method of EngineIoServer."
My question is... EngineIoEndpoint endpoint = new EngineIoEndpoint(); <--- where should this go?
Should it be a) Instantiated in SocketIoServlet.service b) Instantiated in SocketIoServlet.constructor; or c) Instantiated independently during server startup ?
Does it handle the handshake and websocket upgrade process or are we supposed to write those?
EngineIoEndpoint
is instantiated by the server (I know for a fact that Tomcat does) every time a websocket connection is established.
The server is responsible for the websocket handshake and passes the opened connection to a new instance of EngineIoEndpoint
which then encapsulates it and passes it to EngineIoServer
.
Ok, I think I've managed to get something working, The "Network" tab is showing a solid websocket connection, and so far the system is holding together without any hiccups.
General... Request URL: ws://localhost:8080/socket.io/?EIO=3&transport=websocket&sid=N9tq38t Request Method: GET Status Code: 101 Switching Protocols
Response... HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Server: Resin/4.0.63 Sec-WebSocket-Accept: cFWvOrwkd2wtBXUbBsQwy8dOQ6Q= Content-Length: 0 Date: Wed, 03 Jun 2020 01:49:20 GMT
Request Headers... GET ws://localhost:8080/socket.io/?EIO=3&transport=websocket&sid=N9tq38t HTTP/1.1 Host: localhost:8080 Connection: Upgrade Pragma: no-cache Cache-Control: no-cache User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)......... Upgrade: websocket Origin: http://localhost:8080 Sec-WebSocket-Version: 13 Accept-Encoding: gzip, deflate, br Accept-Language: en-NZ,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,pl;q=0.6 Cookie: ###REDACTED### Sec-WebSocket-Key: rPA9lAPeoonCrvGys5UeaQ== Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
package servlet;
import GlobalServlet;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.caucho.websocket.WebSocketListener;
import com.caucho.websocket.WebSocketServletRequest;
import io.socket.emitter.Emitter;
import io.socket.engineio.server.EngineIoServer;
import io.socket.socketio.server.SocketIoNamespace;
import io.socket.socketio.server.SocketIoServer;
import io.socket.socketio.server.SocketIoSocket;
@WebServlet(value = "/socket.io/*", asyncSupported = true)
public class SocketIoServlet extends HttpServlet {
EngineIoServer mEngineIoServer = GlobalServlet.wsEngineIoServer;
SocketIoServer mSocketIoServer = GlobalServlet.wsSocketIoServer;
private static final long serialVersionUID = 1L;
public SocketIoServlet()
{
SocketIoNamespace namespace = mSocketIoServer.namespace("/");
namespace.on("connection", new Emitter.Listener() {
@Override
public void call(Object... args) {
SocketIoSocket socket = (SocketIoSocket) args[0];
System.out.println("[SOCKET.IO] Connection. Args=" + args.length+" Socket id=" + socket.getId());
socket.on("message", new Emitter.Listener() {
@Override
public void call(Object... args) {
socket.send("message", args);
try {
System.out.println("[SOCKET.IO] Message. Args=" + args.length);
System.out.println(args[0]);
} catch (Exception e) {
System.out.println("Error");
}
}
});
socket.on("notify", new Emitter.Listener() {
@Override
public void call(Object... args) {
socket.send("message", args);
try {
System.out.println("[SOCKET.IO] Message. Args=" + args.length);
System.out.println(args[0]);
} catch (Exception e) {
System.out.println("Error");
}
}
});
socket.on("ping", new Emitter.Listener() {
@Override
public void call(Object... args) {
socket.send("pong", args);
System.out.println("[SOCKET.IO] Ping");
}
});
socket.on("pong", new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("[SOCKET.IO] Pong");
}
});
socket.on("error", new Emitter.Listener() {
@Override
public void call(Object... args) {
System.out.println("[SOCKET.IO] Error");
}
});
}
});
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[SOCKET.IO] SERVICE CALL. HttpSession="+request.getSession().getId()+" QueryString="+request.getQueryString());
String header = request.getHeader("upgrade");
String transport = request.getParameter("transport");
//testing: have a look at headers
// Enumeration headers = request.getHeaderNames();
// while (headers.hasMoreElements()) {
// String headername = headers.nextElement().toString();
// System.out.println("Header: " + headername + "=" + request.getHeader(headername));
// }
WebSocketListener listener;
if (!("websocket".equals(header) || transport.equalsIgnoreCase("websocket") )) {
System.out.println("----------[HTTP]----------");
mEngineIoServer.handleRequest(request, response);
} else {
System.out.println("----------[WEBSOCKET]----------");
listener = new MyListener();
WebSocketServletRequest wsReq = (WebSocketServletRequest) request;
System.out.println("Starting listener...");
wsReq.startWebSocket(listener);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[SOCKET.IO] SOCKET.IO: doGet");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("[SOCKET.IO] SOCKET.IO: doPost");
}
public void init() throws ServletException {
System.out.println("[WEBSOCKET] SOCKET.IO: Init");
}
public void destroy() {
System.out.println("[WEBSOCKET] SOCKET.IO: DESTROYED");
}
}
package servlet;
import GlobalServlet;
import com.caucho.websocket.WebSocketContext;
import com.caucho.websocket.WebSocketListener;
import java.io.*;
import java.nio.CharBuffer;
import java.util.logging.Logger;
public class MyListener implements WebSocketListener {
private static final Logger log = Logger.getLogger(MyListener.class.getName());
// Instantiate EngineIoEndpoint during creation of this Listener
private EngineIoEndpoint endpoint = new EngineIoEndpoint();
public void onStart(WebSocketContext context) throws IOException {
log.info("[LISTENER] onStart");
}
public void onReadBinary(WebSocketContext context, InputStream in) throws IOException {
log.info("[LISTENER] onReadBinary: NOT IMPLEMENTED!!!");
}
public void onReadText(WebSocketContext context, Reader r) throws IOException {
StringBuilder sb = new StringBuilder();
CharBuffer cb = CharBuffer.allocate(100);
while(r.read(cb)>=0)
{
cb.flip();
sb.append(cb.toString());
}
log.info("[LISTENER] onReadText: " + sb.toString());
GlobalServlet.wsEngineIoServer.emit("message", sb.toString());
log.info("[LISTENER] EMIT TO ENGINE.IO DONE");
}
public void onClose(WebSocketContext context) throws IOException {
log.info("[LISTENER] onClose");
}
public void onDisconnect(WebSocketContext context) throws IOException {
log.info("[LISTENER] onDisconnect");
}
public void onTimeout(WebSocketContext context) throws IOException {
log.info("[LISTENER] onTimeout");
}
}
public class GlobalServlet extends HttpServlet
{
// This is where we store our shared pools and global data, this object persists as long as the server is running
public static EngineIoServer wsEngineIoServer = new EngineIoServer();
public static SocketIoServer wsSocketIoServer = new SocketIoServer(wsEngineIoServer);
}
<servlet servlet-name='GlobalServlet'
servlet-class='servlet.GlobalServlet'
load-on-startup="1">
</servlet>
<servlet>
<servlet-name>SocketIoServlet</servlet-name>
<servlet-class>servlet.SocketIoServlet</servlet-class>
</servlet>
<servlet-mapping url-pattern='/socket.io' servlet-name='SocketIoServlet'/>
Woops, I spoke too soon. My websockets seem to constantly be in 'pending' state, and data isn't being passed to the browser.
It seems my attempt to instantiate that 'endpoint' might be the issue, I don't know how else I can get it to work.
You mention "The endpoint can be registered by annotation" What exactly is the annotation that should be used?
Have a look at the oracle documentation here. I haven't ever used the annotation before though.
Thanks. I've tried that already, no effect.
In fact annotations don't seem to work full stop, I'm unsure why and quite frankly I'm getting sick of trying to fix it.
So... after a week of trying to get this working, I give up.
I might have to re-evaluate my choice of server engine at some point, because this is just a nightmare. So, yeah, I'm done
Update: I've switched to Tomcat 9, and it has solved quite a few issues, but has exposed a few new issues.
Earlier, you told me to delete the "Engine.io servlet" because it wasn't needed.
However, your EngineIoEndpoint references that servlet! Thus: create(EngineIoEndpoint.class, "/engine.io/")
Now I'm confused again. You're creating a connection to a "not needed" class?
or should that actually be: .create(EngineIoEndpoint.class, "/socket.io/")? Note: I've tried changing to this, but it results in a lot of "xhr poll errors"
I've tried
result.add(ServerEndpointConfig.Builder
.create(EngineIoEndpoint.class, "/engine.io/")
.build());
and
result.add(ServerEndpointConfig.Builder
.create(EngineIoEndpoint.class, "/socket.io/")
.build());
and neither of them result in any events being processed.
Also, the "/socket.io" url path is not functioning at all under tomcat. I tried using the annotation method
@WebServlet(value = "/socket.io/*", asyncSupported = true)
and I also tried defining a servlet-mapping in web.xml,
<servlet>
<servlet-name>SocketIoServlet</servlet-name>
<servlet-class>servlet.SocketIoServlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>SocketIoServlet</servlet-name><url-pattern>/socket.io</url-pattern></servlet-mapping>
but neither method results in a functional path
Admittedly, I'm new to tomcat's quirks, so could you could give me a pointer to get that working?
Incidentally, I also tried newer annotation methods @WebServlet(asyncSupported = true, displayName="SocketIoServlet", urlPatterns = "/socket.io/*")
Still no luck, this is getting silly now
EngineIoServer
is provides the underlying transport that SocketIoServer
works on top of.
All the connections (HTTP, Websocket) are handled by EngineIoServer
. The documentation for engine.io uses the /engine.io/
endpoints but for socket.io, you need to replace those with /socket.io/
. Also, the instances of EngineIoServer
and SocketIoServer
must be the same wherever they're used for it to work properly.
As for tomcat, the servlet mappings are processed in the order they appear in web.xml
. @WebServlet
annotation is much more unpredictable in that matter. More specialized endpoints must go above more generalized endpoints. So, if you put /*
above /socket.io/*
, your requests will never reach the socket.io servlet.
If HTTP is working properly, I'd suggest setting some breakpoints in EngineIoEndpoint.onOpen
to try to debug it. Also, check your browser logs and browser console and those should have some issues if anything isn't working.
You need only one servlet mapped to /socket.io/*
and one websocket endpoint mapped to /socket.io/
that calls EngineIoServer
.
Thank you so much for those tips, I'll try them out tomorrow :-D
Ok, I discovered that SocketIoServlet was definitely initialising, but not serving any http/websocket requests. After a bit of mucking round, I found a solution. Note: I'm using Eclipse+Tomcat9 (latest releases as of June 2020) as my IDE. In order to get SocketIoServlet working as a servlet, I had to change the class definition slightly.
@WebServlet(
urlPatterns = { "/socket.io/*" },
asyncSupported = true
)
public class SocketIoServlet extends HttpServlet implements Servlet {
...
}
So it seems that "implements Servlet" and url-pattern "/socket.io/*" (not "/socket.io") were absolutely necessary. This might be a quirk with Tomcat 9 (or even Eclipse perhaps?), I haven't had time to test it. But I think it's important to note if you ever want to create a troubleshooting section.
Now,
I've altered EngineIoEndpoint to grab the static instance of EngineIoServer from my Global Servlet, as follows:
private EngineIoServer mEngineIoServer = GlobalServlet.wsEngineIoServer
And I've changed one line in ApplicationServerConfig as follows (as you instructed)
result.add(ServerEndpointConfig.Builder
.create(EngineIoEndpoint.class, "/socket.io/")
.build());
and using an implementation of your SocketIoClient, I'm connecting to
ws://mydomain.local:8181/socket.io/?blahblah=1
Apparently it doesn't like empty querystrings and throws null pointer exceptions (hence the blahblah=1)
But still I'm getting the following results in the logs (some of the output is just me printing variable values):
Jun 16, 2020 11:22:01 AM org.apache.catalina.core.ApplicationContext log
INFO: default: DefaultServlet.serveResource: Serving resource '/socket.io/' headers and data
Updated global counters. Total=21758, recent login count=2
SESSION: init: -1
[SOCKET.IO-CLIENT] CONNECT ERROR
0:=io.socket.engineio.client.EngineIOException: xhr poll error
After a couple of hours of tinkering I finally managed to get something working.
After selecting "Run as" on SocketIoServlet, Endpoint finally started working. Weird, I would have expected that file to just automatically compile, but nope it needed some manual encouragement. I don't like that 'feature' at all.
Now I'm back to the old problem of the browser connecting by polling, instead of connecting by websocket. But on a positive note, I'm now seeing messages being passed through endpoint.
******************************
SOCKET.IO Service call
******************************
[SOCKET.IO-SERVER] SERVICE CALL. HttpSession=3B54F1CC7C926430D927EAFD79C519B2 QueryString=EIO=3&transport=polling&t=NAwhWnI&sid=NAwejBb
Request.Protocol=HTTP/1.1
Request.Method=POST
Request.ContentType=text/plain;charset=UTF-8
Header: host=mydomain.local:8181
Header: connection=keep-alive
Header: content-length=3
Header: accept=*/*
Header: dnt=1
Header: user-agent=Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
Header: content-type=text/plain;charset=UTF-8
Header: origin=http://mydomain.local:8181
Header: referer=http://mydomain.local:8181/messages/
Header: accept-encoding=gzip, deflate
Header: accept-language=en-GB,en-US;q=0.9,en;q=0.8
Header: cookie=#REDACTED#
----------[FIN]----------
******************************
SOCKET.IO Service call
******************************
[SOCKET.IO-SERVER] SERVICE CALL. HttpSession=3B54F1CC7C926430D927EAFD79C519B2 QueryString=EIO=3&transport=polling&t=NAwhWnm&sid=NAwejBb
Request.Protocol=HTTP/1.1
Request.Method=GET
Request.ContentType=null
Header: host=mydomain.local:8181
Header: connection=keep-alive
Header: accept=*/*
Header: user-agent=Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
Header: dnt=1
Header: referer=http://mydomain.local:8181/messages/
Header: accept-encoding=gzip, deflate
Header: accept-language=en-GB,en-US;q=0.9,en;q=0.8
Header: cookie=#REDACTED#
----------[FIN]----------
[ENDPOINT] Message (string)
I've gotten to this point, websocket just says "pending"
General Request URL: ws://mydomain.local:8181/socket.io/?EIO=3&transport=websocket&sid=NAxJjkR Request Method: GET Status Code: 101
Response Headers Connection: upgrade Date: Tue, 16 Jun 2020 04:07:02 GMT Sec-WebSocket-Accept: W6pJnRJWtUhcTGXVyE4R9HnweHI= Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15 Upgrade: websocket
Request Headers Accept-Encoding: gzip, deflate Accept-Language: en-GB,en-US;q=0.9,en;q=0.8 Cache-Control: no-cache Connection: Upgrade Host: mydomain.local:8181 Origin: http://mydomain.local:8181 Pragma: no-cache Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits Sec-WebSocket-Key: y5QacU5bDP4lhBQZqCDWbQ== Sec-WebSocket-Version: 13 Upgrade: websocket User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
Query string Parameters EIO: 3 transport: websocket sid: NAxJjkR
I've discovered this quirk with Chrome's Network tab. All websocket connections appear as 'Pending' because websocket is a persistent connection and the 'Time' can only show up when a connection has completed and closed which does not happen for websocket.
Click on the websocket request to bring up the headers information and select the 'Messages' tab and make sure messages are being passed around. At the very least, you will see heartbeat messages like this:
Note that the '/equipment' is my application specific message.
Ok good to know. Unfortunately, the only Websocket messages are:
2probe | 1 | 16:53:22.175 | 3probe | 1 | 16:53:22.214 | 2 | 1 | 16:55:31.541 | 3 | 1 | 16:55:31.544 | 2 | 1 | 16:55:57.537 | 3 | 1 | 16:55:57.567 | 2 | 1 | 16:56:22.569 | 3 | 1 | 16:56:22.570 | 2 | 1 | 16:56:47.571 | 3 | 1 | 16:56:47.573 | 2 | 1 | 16:57:12.575 |
The messages are still sending/receiving via the http polling method
:-(
Those messages are the keep-alive messages sent by the client. If that's working, normal messages should also work. Even after establishing a websocket connection, a few polling messages are sent. You can test if websocket is working properly by sending periodic messages after say every 10 seconds. It should start sending by websocket after the first or second one.
Thanks, I'm aware of all that.
After a day (or so) of checking, rechecking, triple checking, etc etc etc I've discovered the problem is with Chrome!!! It was only showing heartbeats and probes in the websocket entry. Nothing else.
I decided to use Firefox instead, and discovered that messages ARE in fact being sent via websocket.
So... yeah. So that's rather crappy. But the problem is solved now. Thank you for your help.
Next problem on the todo list is: how to handle push messages while the user is clicking between pages. The only half decent solution I can think of is to create a "Service Worker" that will receive messages and queue them for delivery to the browser once the websocket is reestablished. Unfortunately this method would exclude a chunk of my customer base who use old browsers.
Weird. It works fine on my Chrome. Could be an issue with the client library. socket.io-client
version 2.3.0
is what I'm using on my projects and it works on both Chrome and Firefox.
As for the page refresh issue, I have always used socket.io with Angular so I don't have any experience or advice for your use case.
I'm closing this issue. Feel free to re-open it or create a new issue if you encounter any more problems.
I'm testing out your socket.io solution...
I've got 1 Test user, using chrome browser (latest version) + socket.io (latest JS version) Our Resin4 server: Using your "socket.io-client" solution, this server sends out notifications to our test user via the socket server
wsclient.js - connection setup
$(function () {
...
I've tested this setup with node.js+socket-io server.js (a more complete script than the code below) and it works perfectly.
Now, as a test, I decided to disable node.js and try your socket.io-server solution by following your documentation (as best I could), I've modified it slightly, but I get the same quirks whether I follow your instructions explicitly or not. I start my Resin4 server (same server mentioned above, but including the following servlet) It runs ok for a few seconds handling the socket.io-client(java) connection ok, but then it then has major issues (described further below). Note: I also tried let socket = io.connect('ws://localhost:8080/socket.io/?user=test'); with same results
package servlet;
import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import io.socket.emitter.Emitter; import io.socket.engineio.server.EngineIoServer; import io.socket.socketio.server.SocketIoNamespace; import io.socket.socketio.server.SocketIoServer; import io.socket.socketio.server.SocketIoSocket;
@WebServlet("/socket.io/*") public class SocketIoServlet extends HttpServlet {
}
The log file reports...
[SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ [SOCKET.IO] Connection. Args=1 Socket id=N9k8mvJ
(repeats forever(?))
WarningService: Shutdown: TcpSocketLink OutOfMemory [20-06-01 13:11:54.045] {EventThread} Task threw exception java.lang.OutOfMemoryError: GC overhead limit exceeded at io.socket.engineio.client.Transport.onError(Transport.java:63) at io.socket.engineio.client.transports.PollingXHR.access$100(PollingXHR.java:26) at io.socket.engineio.client.transports.PollingXHR$6$1.run(PollingXHR.java:140) at io.socket.thread.EventThread$2.run(EventThread.java:80) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
[20-06-01 13:11:54.045] {resin-port-8080-38} WarningService: Shutdown: TcpSocketLink OutOfMemory Exception in thread "EventThread" java.lang.OutOfMemoryError: GC overhead limit exceeded at io.socket.engineio.client.Transport.onError(Transport.java:63) at io.socket.engineio.client.transports.PollingXHR.access$100(PollingXHR.java:26) at io.socket.engineio.client.transports.PollingXHR$6$1.run(PollingXHR.java:140) at io.socket.thread.EventThread$2.run(EventThread.java:80) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
It looks like the listener is refusing the connection
Is there something I'm missing?? Is there something your documentation is missing? Do you have a demonstration example of -say- a simple echo server that you could share? Some help would be appreciated