I'm trying to simulate client-to-client communication via server using WebSocket.
The idea is: Client A want to send a message to Client B. A sends a message to the server, the server finds out whether B has an active session or not by looking up a session map. Then the server forwards the message to B via B's session.
Both A and B are able to connect to the server and are able to send the message to the server. But the server doesn't send back anything(the @OnMessage
isn't getting triggered). And on the server side, I'm getting this: java.io.IOException: An existing connection was forcibly closed by the remote host
And on the client side I get this thrown in the OnError()
method:
java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.String
at org.glassfish.tyrus.core.TyrusSession.notifyMessageHandlers(TyrusSession.java:576)
at org.glassfish.tyrus.core.TyrusEndpointWrapper.onMessage(TyrusEndpointWrapper.java:871)
at org.glassfish.tyrus.core.TyrusWebSocket.onMessage(TyrusWebSocket.java:212)
at org.glassfish.tyrus.core.frame.TextFrame.respond(TextFrame.java:139)
at org.glassfish.tyrus.core.ProtocolHandler.process(ProtocolHandler.java:807)
at org.glassfish.tyrus.client.TyrusClientEngine$TyrusReadHandler.handle(TyrusClientEngine.java:747)
at org.glassfish.tyrus.container.jdk.client.ClientFilter.processRead(ClientFilter.java:226)
at org.glassfish.tyrus.container.jdk.client.Filter.onRead(Filter.java:134)
at org.glassfish.tyrus.container.jdk.client.Filter.onRead(Filter.java:136)
at org.glassfish.tyrus.container.jdk.client.Filter.onRead(Filter.java:136)
at org.glassfish.tyrus.container.jdk.client.TransportFilter$4.completed(TransportFilter.java:299)
at org.glassfish.tyrus.container.jdk.client.TransportFilter$4.completed(TransportFilter.java:283)
at sun.nio.ch.Invoker.invokeUnchecked(Invoker.java:126)
at sun.nio.ch.Invoker$2.run(Invoker.java:218)
at sun.nio.ch.AsynchronousChannelGroupImpl$1.run(AsynchronousChannelGroupImpl.java:112)
This is what I get from Wireshark:
No. Time Source Destination Protocol Length Info
5065 23.807645 127.0.0.1 127.0.0.1 TCP 108 50765 → 8080 [SYN] Seq=0 Win=64240 Len=0 MSS=65495 WS=256 SACK_PERM=1
Frame 5065: 108 bytes on wire (864 bits), 56 bytes captured (448 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50765, Dst Port: 8080, Seq: 0, Len: 0
No. Time Source Destination Protocol Length Info
5066 23.807689 127.0.0.1 127.0.0.1 TCP 108 8080 → 50765 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=65495 WS=256 SACK_PERM=1
Frame 5066: 108 bytes on wire (864 bits), 56 bytes captured (448 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 8080, Dst Port: 50765, Seq: 0, Ack: 1, Len: 0
No. Time Source Destination Protocol Length Info
5067 23.807727 127.0.0.1 127.0.0.1 TCP 84 50765 → 8080 [ACK] Seq=1 Ack=1 Win=525568 Len=0
Frame 5067: 84 bytes on wire (672 bits), 44 bytes captured (352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50765, Dst Port: 8080, Seq: 1, Ack: 1, Len: 0
No. Time Source Destination Protocol Length Info
5070 23.814122 127.0.0.1 127.0.0.1 HTTP 454 GET /ChatApp/chat/D HTTP/1.1
Frame 5070: 454 bytes on wire (3632 bits), 229 bytes captured (1832 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50765, Dst Port: 8080, Seq: 1, Ack: 1, Len: 185
Hypertext Transfer Protocol
No. Time Source Destination Protocol Length Info
5071 23.814169 127.0.0.1 127.0.0.1 TCP 84 8080 → 50765 [ACK] Seq=1 Ack=186 Win=525568 Len=0
Frame 5071: 84 bytes on wire (672 bits), 44 bytes captured (352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 8080, Dst Port: 50765, Seq: 1, Ack: 186, Len: 0
No. Time Source Destination Protocol Length Info
5074 23.815741 127.0.0.1 127.0.0.1 HTTP 584 HTTP/1.1 101 Switching Protocols
Frame 5074: 584 bytes on wire (4672 bits), 294 bytes captured (2352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 8080, Dst Port: 50765, Seq: 1, Ack: 186, Len: 250
Hypertext Transfer Protocol
No. Time Source Destination Protocol Length Info
5075 23.815771 127.0.0.1 127.0.0.1 TCP 84 50765 → 8080 [ACK] Seq=186 Ack=251 Win=525312 Len=0
Frame 5075: 84 bytes on wire (672 bits), 44 bytes captured (352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50765, Dst Port: 8080, Seq: 186, Ack: 251, Len: 0
No. Time Source Destination Protocol Length Info
5078 23.843679 127.0.0.1 127.0.0.1 TCP 108 50766 → 8080 [SYN] Seq=0 Win=64240 Len=0 MSS=65495 WS=256 SACK_PERM=1
Frame 5078: 108 bytes on wire (864 bits), 56 bytes captured (448 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50766, Dst Port: 8080, Seq: 0, Len: 0
No. Time Source Destination Protocol Length Info
5079 23.843738 127.0.0.1 127.0.0.1 TCP 108 8080 → 50766 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=65495 WS=256 SACK_PERM=1
Frame 5079: 108 bytes on wire (864 bits), 56 bytes captured (448 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 8080, Dst Port: 50766, Seq: 0, Ack: 1, Len: 0
No. Time Source Destination Protocol Length Info
5080 23.843793 127.0.0.1 127.0.0.1 TCP 84 50766 → 8080 [ACK] Seq=1 Ack=1 Win=525568 Len=0
Frame 5080: 84 bytes on wire (672 bits), 44 bytes captured (352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50766, Dst Port: 8080, Seq: 1, Ack: 1, Len: 0
No. Time Source Destination Protocol Length Info
5082 23.844179 127.0.0.1 127.0.0.1 HTTP 454 GET /ChatApp/chat/A HTTP/1.1
Frame 5082: 454 bytes on wire (3632 bits), 229 bytes captured (1832 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50766, Dst Port: 8080, Seq: 1, Ack: 1, Len: 185
Hypertext Transfer Protocol
No. Time Source Destination Protocol Length Info
5084 23.844213 127.0.0.1 127.0.0.1 TCP 84 8080 → 50766 [ACK] Seq=1 Ack=186 Win=525568 Len=0
Frame 5084: 84 bytes on wire (672 bits), 44 bytes captured (352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 8080, Dst Port: 50766, Seq: 1, Ack: 186, Len: 0
No. Time Source Destination Protocol Length Info
5085 23.845363 127.0.0.1 127.0.0.1 HTTP 584 HTTP/1.1 101 Switching Protocols
Frame 5085: 584 bytes on wire (4672 bits), 294 bytes captured (2352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 8080, Dst Port: 50766, Seq: 1, Ack: 186, Len: 250
Hypertext Transfer Protocol
No. Time Source Destination Protocol Length Info
5086 23.845406 127.0.0.1 127.0.0.1 TCP 84 50766 → 8080 [ACK] Seq=186 Ack=251 Win=525312 Len=0
Frame 5086: 84 bytes on wire (672 bits), 44 bytes captured (352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50766, Dst Port: 8080, Seq: 186, Ack: 251, Len: 0
No. Time Source Destination Protocol Length Info
5102 24.168567 127.0.0.1 127.0.0.1 WebSocket 212 WebSocket Text [FIN] [MASKED]
Frame 5102: 212 bytes on wire (1696 bits), 108 bytes captured (864 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50765, Dst Port: 8080, Seq: 186, Ack: 251, Len: 64
WebSocket
Line-based text data (1 lines)
No. Time Source Destination Protocol Length Info
5103 24.168606 127.0.0.1 127.0.0.1 WebSocket 212 WebSocket Text [FIN] [MASKED]
Frame 5103: 212 bytes on wire (1696 bits), 108 bytes captured (864 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50766, Dst Port: 8080, Seq: 186, Ack: 251, Len: 64
WebSocket
Line-based text data (1 lines)
No. Time Source Destination Protocol Length Info
5104 24.168610 127.0.0.1 127.0.0.1 TCP 84 8080 → 50765 [ACK] Seq=251 Ack=250 Win=525312 Len=0
Frame 5104: 84 bytes on wire (672 bits), 44 bytes captured (352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 8080, Dst Port: 50765, Seq: 251, Ack: 250, Len: 0
No. Time Source Destination Protocol Length Info
5105 24.168648 127.0.0.1 127.0.0.1 TCP 84 8080 → 50766 [ACK] Seq=251 Ack=250 Win=525312 Len=0
Frame 5105: 84 bytes on wire (672 bits), 44 bytes captured (352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 8080, Dst Port: 50766, Seq: 251, Ack: 250, Len: 0
No. Time Source Destination Protocol Length Info
5110 24.171796 127.0.0.1 127.0.0.1 WebSocket 110 WebSocket Text [FIN]
Frame 5110: 110 bytes on wire (880 bits), 57 bytes captured (456 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 8080, Dst Port: 50766, Seq: 251, Ack: 250, Len: 13
WebSocket
Line-based text data (1 lines)
No. Time Source Destination Protocol Length Info
5111 24.171827 127.0.0.1 127.0.0.1 TCP 84 50766 → 8080 [ACK] Seq=250 Ack=264 Win=525312 Len=0
Frame 5111: 84 bytes on wire (672 bits), 44 bytes captured (352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50766, Dst Port: 8080, Seq: 250, Ack: 264, Len: 0
No. Time Source Destination Protocol Length Info
5112 24.172014 127.0.0.1 127.0.0.1 WebSocket 110 WebSocket Text [FIN]
Frame 5112: 110 bytes on wire (880 bits), 57 bytes captured (456 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 8080, Dst Port: 50765, Seq: 251, Ack: 250, Len: 13
WebSocket
Line-based text data (1 lines)
No. Time Source Destination Protocol Length Info
5113 24.172037 127.0.0.1 127.0.0.1 TCP 84 50765 → 8080 [ACK] Seq=250 Ack=264 Win=525312 Len=0
Frame 5113: 84 bytes on wire (672 bits), 44 bytes captured (352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50765, Dst Port: 8080, Seq: 250, Ack: 264, Len: 0
No. Time Source Destination Protocol Length Info
5115 24.525813 127.0.0.1 127.0.0.1 TCP 84 50765 → 8080 [RST, ACK] Seq=250 Ack=264 Win=0 Len=0
Frame 5115: 84 bytes on wire (672 bits), 44 bytes captured (352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50765, Dst Port: 8080, Seq: 250, Ack: 264, Len: 0
No. Time Source Destination Protocol Length Info
5118 24.562668 127.0.0.1 127.0.0.1 TCP 84 50766 → 8080 [RST, ACK] Seq=250 Ack=264 Win=0 Len=0
Frame 5118: 84 bytes on wire (672 bits), 44 bytes captured (352 bits) on interface 0
Null/Loopback
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 50766, Dst Port: 8080, Seq: 250, Ack: 264, Len: 0
The last two Wireshark entries show that the client resets the connection(maybe this is why the server is raising the IOException
)
This is my client side code:
public class WebSocketClientEndpoint extends Endpoint {
private final String username;
private Session session;
public WebSocketClientEndpoint(final String username, final String URLString) throws URISyntaxException, IOException, DeploymentException {
this.username = username;
ContainerProvider.getWebSocketContainer().connectToServer(this, new URI(URLString));
}
@Override
public void onOpen(Session session, EndpointConfig endpointConfig) {
this.session = session;
// this.session.setMaxIdleTimeout(500L);
System.out.println("Connected to " + this.session.getId());
this.session.addMessageHandler((MessageHandler.Whole<String>) message -> {
ObjectMapper mapper = new ObjectMapper();
Map<String, String> map;
try {
map = mapper.readValue(message, new TypeReference<Map<String, String>>(){});
System.out.println(map.getOrDefault("message","NOTHING!!!"));
} catch (IOException e) {
e.printStackTrace();
}
});
}
@Override
public void onClose(Session session, CloseReason closeReason) {
super.onClose(session, closeReason);
System.out.println("this part executing");
System.out.println(session.getId() + " " + closeReason.getReasonPhrase());
}
@Override
public void onError(Session session, Throwable thr) {
System.out.println("Getting Errors here");
thr.printStackTrace();
}
public void sendMessage(String message, String destination) throws IOException {
if(this.session!=null) {
ObjectMapper mapper = new ObjectMapper();
HashMap<String,String> messageMap = new HashMap<>();
messageMap.put("username", this.username);
messageMap.put("destination",destination);
messageMap.put("message",message);
String payload = mapper.writeValueAsString(messageMap);
if (session.isOpen()) new Thread(() -> this.session.getAsyncRemote().sendText(payload)).start();
else System.out.println(session.getId() + " is closed");
}
else System.out.println("Send to whom?");
}
public void closeSession() throws IOException {
this.session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Normal closure"));
}
}
public static void main(String[] args) throws DeploymentException, IOException, URISyntaxException {
WebSocketClientEndpoint webSocketClientEndpoint1 = new WebSocketClientEndpoint("D","ws://localhost:8080/ChatApp/chat/D");
WebSocketClientEndpoint webSocketClientEndpoint2 = new WebSocketClientEndpoint("A","ws://localhost:8080/ChatApp/chat/A");
webSocketClientEndpoint1.sendMessage("From D to A", "A");
webSocketClientEndpoint2.sendMessage("From A to D", "D");
}
This is my server side code:
@ServerEndpoint(value = "/chat/{username}")
public class WebSocketServerEndpoint {
private static Map<String,Session> SESSION_MAP = Collections.synchronizedMap(new HashMap<>());
@OnOpen
public void handleOpen(Session session, @PathParam("username") String pathName) {
SESSION_MAP.putIfAbsent(pathName,session);
// session.getAsyncRemote().sendText(session + " " + pathName + " connected");
}
@OnMessage
public void handleMessage(String message, Session session) {
ObjectMapper mapper = new ObjectMapper();
Map<String,String> messageMap;
try {
messageMap = mapper.readValue(message, new TypeReference<Map<String, String>>(){});
String destination = messageMap.get("destination");
Session destinationSession = SESSION_MAP.get(destination);
if(destinationSession!=null)
destinationSession.getBasicRemote()
.sendText(messageMap.getOrDefault("message","NoMessageToSend"));
} catch (IOException e) {
e.printStackTrace();
}
}
@OnClose
public void handleClose(Session session, CloseReason closeReason) {
String username = session.getPathParameters().get("username");
SESSION_MAP.remove(username);
}
@OnError
public void handleError(Session session, Throwable thr) {
thr.printStackTrace();
}
}
I tried running it on Glassfish first but there wasn't any proper error message. Then I ran it on Wildfly and got the IOException
.
My questions are:
1. How can one client talk to another client through a server using WebSocket protocol in context of my implementation?
2. What are the problems in my implementation?
UPDATE
So I found out the reason for the ClassCastException
. I was trying to parse a plain string into JSON
. I fixed it and this is my new Client Endpoint:
@ClientEndpoint
public class WebSocketClientEndpoint {
private final String username;
private Session session;
public WebSocketClientEndpoint(final String username, final String URLString) throws URISyntaxException, IOException, DeploymentException {
this.username = username;
ContainerProvider.getWebSocketContainer().connectToServer(this, new URI(URLString));
}
@OnOpen
public void handleOpen(Session session) {
this.session = session;
System.out.println("Connected to " + this.session.getId());
}
@OnMessage
public void handleMessage(String message) {
System.out.println("Executing Here in onMessage() callback");
System.out.println(message);
}
@OnClose
public void handleClose(Session session, CloseReason closeReason) {
System.out.println("this part executing");
System.out.println(session.getId() + " " + closeReason.getReasonPhrase());
}
@OnError
public void handleError(Session session, Throwable thr) {
System.out.println("Getting Errors here");
thr.printStackTrace();
}
public void sendMessage(String message, String destination) throws IOException {
if(this.session!=null) {
ObjectMapper mapper = new ObjectMapper();
HashMap<String,String> messageMap = new HashMap<>();
messageMap.put("username", this.username);
messageMap.put("destination",destination);
messageMap.put("message",message);
String payload = mapper.writeValueAsString(messageMap);
if (session.isOpen()) new Thread(() -> this.session.getAsyncRemote().sendText(payload)).start();
else System.out.println(session.getId() + " is closed");
}
else System.out.println("Send to whom?");
}
public void closeSession() throws IOException {
this.session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Normal closure"));
}
}
Now the issue is that the onMessage()
callback has an getting invoked sometimes and isn't getting invoked sometimes. And when it is getting invoked, the message from lets say A to B is displayed, from B to A isn't.
Why is this type of thing happening even though there are two different clients? Shouldn't both the messages get displayed. Both the clients are being simulated from the same Java console application.
Update
I have resolved the issue regarding only one client's message being displayed by removing a processing of messages on the server on a new thread each time a message comes in(I later changed my code). But one issue still remains, which is that there are still instances when only one client's message is displayed(although it's around once in 4 executions).
I want to get an understanding about the cause of such behaviour