我正在尝试使用WebSocket模拟服务器之间的客户端到客户端通信。
这个想法是: 客户 A 要向客户 B 发送消息。 A 向服务器发送一条消息,服务器通过查找会话映射来找出 B 是否具有活动会话。然后,服务器通过 B 的会话将邮件转发到 B 。
A 和 B 都能够连接到服务器,并且能够将消息发送到服务器。但是服务器没有发回任何东西(@OnMessage
没有被触发)。在服务器端,我得到了这个:java.io.IOException: An existing connection was forcibly closed by the remote host
在客户端,我将这种情况放在OnError()
方法中:
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)
这是我从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
最后两个Wireshark条目显示客户端重置了连接(也许这就是服务器提升IOException
的原因)
这是我的客户端代码:
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");
}
这是我的服务器端代码:
@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();
}
}
我首先尝试在Glassfish上运行它,但是没有任何正确的错误消息。然后,我在Wildfly上运行了它,并得到了IOException
。
我的问题是:
1。在我的实现上下文中,一个客户端如何通过使用WebSocket协议的服务器通过服务器与另一客户端对话?
2。我的实施中存在哪些问题?
更新
因此,我找出了ClassCastException
的原因。我试图将纯字符串解析为JSON
。我已修复它,这是我的新客户端端点:
@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"));
}
}
现在的问题是onMessage()
回调有时会被调用,而有时不会被调用。当它被调用时,会显示从 A 到 B 的消息,从 B 到 A 的消息。不是。
即使有两个不同的客户端,为什么会发生这种情况?不应同时显示两条消息。这两个客户端都从同一Java控制台应用程序进行模拟。
更新
我已经解决了仅在显示一条消息时就出现的问题,方法是每次收到一条消息时就在新线程上删除服务器上的消息处理(我后来更改了代码)。但是仍然存在一个问题,那就是在某些情况下,仅显示一个客户端的消息(尽管大约有四次执行一次)。
我想了解这种行为的原因