1

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

Shankha057
  • 1,296
  • 1
  • 20
  • 38
  • You should be investigating the `ClassCastException`. That caused the reset. – user207421 Jul 11 '18 at 05:09
  • @EJP The `ClassCastException` is being thrown totally at random. The rate is lower when the server starts for the first time(or restarts). And the the error is coming from `OnError` method. – Shankha057 Jul 11 '18 at 05:15
  • I also noticed that the server sends back the clients the data(showing in Wireshark) but in the client the `OnMessage` callback isn't triggered and fter that, the clients are resetting he connection and sometimes throwing the `ClassCastException` at random, which is caught in the `OnError` callback. – Shankha057 Jul 11 '18 at 05:21
  • `onError()` doesn't appear in the stack trace. – user207421 Jul 11 '18 at 07:05
  • `OnError()` doesn't appear in the stack trace but it is thrown from there. I'll update the question now. I switched from using programmatic endpoints to annotated endpoints and the `ClassCastException` is gone, and sometimes the `OnMessage()` callback is getting invoked. – Shankha057 Jul 11 '18 at 07:25

0 Answers0