【问题标题】:WebSocket Closes with Protocol Error 1002WebSocket 关闭并出现协议错误 1002
【发布时间】:2021-03-23 22:26:44
【问题描述】:

我正在实现 WebSocket 消息命令行客户端。 我已检查此错误是否与协议问题相对应。我将ws升级到最新的7.4.1。在后端,我使用 2.3.4.RELEASE 版本的 Spring Boot Websockets。

据说这两个主要原因是数据包丢失或格式错误的消息。 我做了一些检查来检查这些,但似乎没有一个是有效的。 我测试的消息很小,因此消息大小不应该是这种情况。连接完全在本地主机上。 我用 3 个用户测试了这个解决方案,有时我得到这个错误,有时却没有。

谁能帮我弄清楚如何摆脱这种类型的错误?

这是我用于客户端发送消息的代码:

async function test(number_of_messages, break_between_messages) {
const websocket = new WebSocket(url...)

websocket.on('message', function incoming(data) {
    console.log(getMessage("Received", data))
});

websocket.on('close', function(data) {
    console.log('Disconnected!!!! ' + data.toString());
});

const opened = await connection(websocket)

//Wait 5 seconds
await sleep(5_000);

if (opened) {
    for (i = 0; i < number_of_messages; i++) {

        for (const chatId of chatIds) {
            let content = i.toString() + " from " + user;
            let msg = JSON.stringify({
                "chatId": chatId,
                "author": user,
                "content": content
            })
            websocket.send(msg)

            let message = getMessage("Sent", msg)
            console.log(message)
        }

        await sleep(break_between_messages);
    }

} else {
    console.log("ERROR on Opening Connection")
    return
}

// Wait 1 minute
await sleep(60_000);
websocket.close()

}

带后端代码:

@Component
@ServerEndpoint(value = "/webSocket/{username}",
        encoders = MessageRepresentationEncoder.class, decoders = MessageRepresentationDecoder.class)
public class MessagingSocket {
    private Logger logger = LoggerFactory.getInstance();
    private Session session;
    private MessagingAPI messagingAPI = MessagingAPIFactory.createAPI();
    private UserSocketRegistry userSocketRegistry = UserSocketRegistry.createRegistry();
    private SessionUserRegistry sessionUserRegistry = SessionUserRegistry.createRegistry();

    @OnOpen
    public void onOpen(Session session, @PathParam("username") String username) {
        this.session = session;
        logger.log(LoggingType.INFO, "Started new session " + session.getId());
        logger.log(LoggingType.INFO, username + " connected");

        userSocketRegistry.addSessionForUser(this, username);
        sessionUserRegistry.addSessionForUser(session, username);
    }

    @OnMessage //Allows the client to send message to the socket.
    public void onMessage(MessageRepresentation messageRepresentation) {
        logger.log(LoggingType.INFO, "Received " + messageRepresentation.toString());
        messagingAPI.write(WriteMessage.from(UUID.fromString(messageRepresentation.chatId), messageRepresentation.author, messageRepresentation.content));
        broadcastToChat(messageRepresentation);
    }

    private void broadcastToChat(MessageRepresentation message) {
        final List<MessagingSocket> sockets = messagingAPI.getUsersConnectedToChat(UUID.fromString(message.chatId)).stream().filter(user -> userSocketRegistry.hasSocketFor(user.getName()))
                .map(user -> userSocketRegistry.getSocketFor(user.getName())).collect(Collectors.toList());

        logger.log(LoggingType.INFO, "Starting broadcast of " + message.content + " from " + message.author + " for " + String.join(",", messagingAPI.getUsersConnectedToChat(UUID.fromString(message.chatId)).stream().map(x -> x.getName()).collect(Collectors.toList())));
        for (MessagingSocket messagingSocket : sockets) {
            logger.log(LoggingType.INFO, "Broadcasting message" + message.content + " to " + messagingSocket.session.getId());
            messagingSocket.sendMessage(message);

        }
    }

    private void sendMessage(MessageRepresentation message) {
        try {
            this.session.getBasicRemote().sendObject(message);
        } catch (IOException | EncodeException e) {
            logger.log(LoggingType.ERROR, "Caught exception while sending message to Session Id: " + this.session.getId());
        }
    }

    @OnClose
    public void onClose(Session session) {
        String user = sessionUserRegistry.getUserFor(session);
        logger.log(LoggingType.INFO, "User " + user + " with session " + this.session.getId() + " disconnected ");
        sessionUserRegistry.removeSession(session);
        userSocketRegistry.removeUser(user);
    }
}

而MessageRepresentation为:

public class MessageRepresentation {
    public String chatId;
    public String author;
    public String content;

    @Override
    public String toString() {
        return "MessageRepresentation{" +
                "chatId='" + chatId + '\'' +
                ", author='" + author + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}

经过进一步调查,我得到以下异常:

java.lang.IllegalStateException: The remote endpoint was in state [TEXT_FULL_WRITING] which is an invalid state for called method
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.checkState(WsRemoteEndpointImplBase.java:1243)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.textStart(WsRemoteEndpointImplBase.java:1205)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendString(WsRemoteEndpointImplBase.java:191)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendObject(WsRemoteEndpointImplBase.java:600)
at org.apache.tomcat.websocket.WsRemoteEndpointBasic.sendObject(WsRemoteEndpointBasic.java:74)
at presentation.frontend.websockets.server.MessagingSocket.sendMessage(MessagingSocket.java:64)
at presentation.frontend.websockets.server.MessagingSocket.broadcastToChat(MessagingSocket.java:57)
at presentation.frontend.websockets.server.MessagingSocket.onMessage(MessagingSocket.java:47)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.apache.tomcat.websocket.pojo.PojoMessageHandlerWholeBase.onMessage(PojoMessageHandlerWholeBase.java:80)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:402)
at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:119)
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:502)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:301)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:82)
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:171)
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:151)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:148)
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:54)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:834)

java.lang.IllegalStateException: 消息不会被发送,因为 WebSocket 会话已经关闭

【问题讨论】:

  • 也请分享一下后端websocket相关代码
  • 我已经包含了端点代码
  • _ 中的awaits 真的是语法的一部分吗?我想我从来没有见过。并不是那应该导致您遇到的错误。您能否总结一下您要复制的内容以及在引发错误之前的最后一行功能代码是哪一行?
  • 如果我理解正确,您能否参考 awaits 评论的确切行这与此处v8.dev/features/numeric-separators 的用途相同?我启动服务器并重新运行其余部分,如此处所示,然后在几条消息之后,我可以看到客户端的 onClose 部分中的日志。平均有 70% 的时间发生在一个代理身上。我使用 3 个代理进行测试。
  • 您可能应该标记您正在使用的框架,以吸引更多针对您的问题的视图。

标签: javascript spring-boot websocket ws


【解决方案1】:

我在这里可能不正确,但我认为这可能是由于包含无效 UTF8 或类似内容的消息。即格式错误。

如果这听起来可能是原因,那么简单的修复方法是编码 msg

        let msg = JSON.stringify({
            "chatId": chatId,
            "author": user,
            "content": content
        })

  let msg = unescape(encodeURIComponent(JSON.stringify({
                "chatId": chatId,
                "author": user,
                "content": content
            })));

然后在另一边解码...

JSON.parse(decodeURIComponent(escape( ... )))

【讨论】:

  • 看起来我仍然让一些客户端断开连接(这里是代理 1 日志的一部分):[1608576813254]Received: {"chatId":"0ea9c68c-fe9a-4bc6-adc9-0068cab1b013", "author":"Agent3","content":"18 from Agent3"} 断开连接!!! 1002
  • 因为你的回答可能很重要,所以我要给你赏金。我将在 Wireshark 中检查消息格式错误的原因。
  • 我在执行此测试时查看了 Wireshark:我可以看到 WebSocket Connection Close [FIN],当我查看数据时显示“发生了不可恢复的 IOException,因此连接已关闭”;此消息从我的服务器发送到客户端
【解决方案2】:

解决这个问题的方法包括 2 个步骤。

1:查找错误堆栈跟踪

@OnError
public void onError(Session session, Throwable throwable) {
    logger.log(LoggingType.ERROR, "Error for " + session.getId() + " caused by: " + throwable.getMessage());
    throwable.printStackTrace();
}

2:在广播消息中将同步的BasicRemote改为异步的AsyncRemote(这在消息数量增加时很重要)

private void sendMessage(MessageRepresentation message) {
        this.session.getAsyncRemote().sendObject(message);
    }

【讨论】:

    猜你喜欢
    • 2016-03-13
    • 2014-05-03
    • 1970-01-01
    • 1970-01-01
    • 2014-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-14
    相关资源
    最近更新 更多