简体   繁体   English

WebSocket 关闭并出现协议错误 1002

[英]WebSocket Closes with Protocol Error 1002

I m implementing WebSocket messages command-line client.我正在实现 WebSocket 消息命令行客户端。 I have checked that this error corresponds to the problem with the protocol.我已检查此错误是否与协议问题相对应。 I upgraded ws to the newest 7.4.1.我将ws升级到最新的7.4.1。 At backend I use Spring Boot Websockets at version 2.3.4.RELEASE.在后端,我使用 Spring Boot Websockets 版本为 2.3.4.RELEASE。

The 2 main causes of this are said to be packet loss or malformed messages.据说造成这种情况的两个主要原因是数据包丢失或格式错误的消息。 I have made some checks to check those but none seem valid.我做了一些检查来检查这些,但似乎没有一个是有效的。 The messages I test are small so it shouldn't be the case with message size.我测试的消息很小,因此消息大小不应该是这种情况。 The connection is fully on localhost.连接完全在本地主机上。 I test the solution with 3 users and sometimes I get this error sometimes not.我用 3 个用户测试解决方案,有时我会收到此错误,有时不会。

Can someone help me figure out how to get rid of this type of error?有人可以帮我弄清楚如何摆脱这种类型的错误吗?

Here is the code I use for client to send messages:这是我用于客户端发送消息的代码:

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()

} }

With backend code:使用后端代码:

@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);
    }
}

And MessageRepresentation as:和 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 + '\'' +
                '}';
    }
}

After further investigation I m getting the following exception:经过进一步调查,我得到以下异常:

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: Message will not be sent because the WebSocket session has been closed java.lang.IllegalStateException:消息将不会发送,因为 WebSocket session 已关闭

I could be off the mark here but I think this is probably due to the message containing invalid UTF8 or some such.我在这里可能不合时宜,但我认为这可能是由于包含无效 UTF8 或类似内容的消息。 ie malformed.即畸形。

If this sounds like it could be the cause the simple fix would be to encode the msg如果这听起来像它可能是简单的修复将是编码msg的原因

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

to

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

Then decode on the other side...然后在另一边解码...

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

The solution to this one involved 2 steps.这个解决方案涉及两个步骤。

1: Find the error stacktrace 1:查找错误堆栈跟踪

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

2: Change synchronous BasicRemote to asynchronous AsyncRemote in broadcasting messages (this is important when the number of messages increased) 2:在广播消息中将同步的BasicRemote改为异步的AsyncRemote(这在消息数增加时很重要)

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM