java WebSocket 远程端点处于 [TEXT_PARTIAL_WRITING] 状态

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29002063/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 14:31:58  来源:igfitidea点击:

WebSocket The remote endpoint was in state [TEXT_PARTIAL_WRITING]

javaspring

提问by mc j

I'm using tomcat 8.0.15, spring 4.1.5.

我使用的是 tomcat 8.0.15,spring 4.1.5。

I implemented 3 mandatory functions for using websocket like below. It's very simple.

我实现了 3 个使用 websocket 的强制性功能,如下所示。这很简单。

private Map<String, WebSocketSession> map_users = new ConcurrentHashMap<>();
private Map<String, String> map_id = new ConcurrentHashMap<>();

public void afterConnectionEstablished(WebSocketSession wss) throws Exception {
    map_users.put(wss.getId(), wss);
}

public void afterConnectionClosed(WebSocketSession wss, CloseStatus cs) throws Exception {
    map_users.remove(wss.getId());

    // remove user
    String username = map_id.get(wss.getId());
    if (username != null) {
        map_id.remove(wss.getId());
        map_id.remove(username);
    }
}

public void handleTextMessage(WebSocketSession wss, TextMessage tm) throws Exception {
    String str = tm.getPayload();
    String username = ...;

    // regist user
    if (!map_id.get(wss.getId())) {
        map_id.put(wss.getId(), username);
        map_id.put(username, wss.getId());
    }

    for (WebSocketSession w: map_users.values()) {
        w.sendMessage(new TextMessage(wss.getId() + " send to " + w.getId() + ", msg:" + tm.getPayload()));
    }
}

Some client sends a message and other clients get the message by the handleTextMessage.

一些客户端发送消息,其他客户端通过 handleTextMessage 获取消息。

In my case, without the handleTextMessage function, server program wants to send a text message to clients. (for this I saved a WebSocketSession's Id and username into map_id)

在我的情况下,没有 handleTextMessage 函数,服务器程序想要向客户端发送文本消息。(为此,我将 WebSocketSession 的 Id 和用户名保存到 map_id 中)

String websocketsesssion_id = map_id.get(username);
WebSocketSession wss = map_users.get(websocketsesssion_id);
wss.sendMessage(new TextMessage(new java.util.Date()));

Above code works very well. But when some client's WebSocketSession is in use and try to use simultaneously, it makes error. It means 1. some client send message --> handleTextMessage is called --> the client's WebSocketSession is using 2. server program wants to send message to that client --> get the client's WebSocketSession from map --> try to send message with the same WebSocketSession

上面的代码效果很好。但是当某些客户端的 WebSocketSession 正在使用并尝试同时使用时,它会出错。这意味着 1. 某些客户端发送消息 --> 调用 handleTextMessage --> 客户端的 WebSocketSession 正在使用 2. 服务器程序想要向该客户端发送消息 --> 从地图中获取客户端的 WebSocketSession --> 尝试发送消息相同的 WebSocketSession

Stacktrace:] with root cause
java.lang.IllegalStateException: The remote endpoint was in state [TEXT_PARTIAL_WRITING] which is an invalid state for called method
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.checkState(WsRemoteEndpointImplBase.java:1092)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.textPartialStart(WsRemoteEndpointImplBase.java:1050)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendPartialString(WsRemoteEndpointImplBase.java:218)
    at org.apache.tomcat.websocket.WsRemoteEndpointBasic.sendText(WsRemoteEndpointBasic.java:49)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketSession.sendTextMessage(StandardWebSocketSession.java:197)
    at org.springframework.web.socket.adapter.AbstractWebSocketSession.sendMessage(AbstractWebSocketSession.java:105)
    at org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession.writeFrameInternal(WebSocketServerSockJsSession.java:222)
    at org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession.writeFrame(AbstractSockJsSession.java:325)
    at org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession.sendMessageInternal(WebSocketServerSockJsSession.java:212)
    at org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession.sendMessage(AbstractSockJsSession.java:161)

As the result, WebSocketSession is closed and client has to open new WebSocketSession again.

结果,WebSocketSession 关闭,客户端必须再次打开新的 WebSocketSession。

So, my question is:

所以,我的问题是:

Can I check whether the WebSocketSession is in use or not? (outside the handleTextMessage function)

我可以检查 WebSocketSession 是否正在使用中吗?(在handleTextMessage 函数之外)

回答by user3179515

The problem is that several thread try to write in same time on the socket so :

问题是多个线程尝试在套接字上同时写入,因此:

try :

尝试 :

String websocketsesssion_id = map_id.get(username);
WebSocketSession wss = map_users.get(websocketsesssion_id);
synchronized(wss) {
 wss.sendMessage(new TextMessage(new java.util.Date()));
}

回答by Oleg Fraimovich

While synchronizing sendMessage()calls helps for ws:// connection, it doesn't help for wss://, i.e. SSL/TLS connection. It is 100% failure if you trying send partial messages while reading partial messages. I have tested it on Spring Boot 1.4.2 RELEASE (tomcat 8.5.6)

虽然同步sendMessage()调用对 ws:// 连接有帮助,但对wss://, i.e. SSL/TLS connection. 如果您在阅读部分消息时尝试发送部分消息,则 100% 失败。我已经在 Spring Boot 1.4.2 RELEASE (tomcat 8.5.6) 上测试过了

The same with tomcat 8.5.9. See the trace stack below.

与 tomcat 8.5.9 相同。请参阅下面的跟踪堆栈。

[pool-18-thread-1] [GenericMessageEndpoint] Sending message to the endpoint: 
[pool-18-thread-2] [GenericMessageEndpoint] Sending message to the endpoint: 
[pool-18-thread-3] [GenericMessageEndpoint] Sending message to the endpoint: 

[https-jsse-nio-8443-exec-1] [FrontendWebSocketHandler] Message from 0, isLast: false, length: 16384
[https-jsse-nio-8443-exec-1] [FrontendWebSocketHandler] Message from 0, isLast: false, length: 16384

[pool-18-thread-3] [FrontendWebSocketHandler] Transport error. Session: StandardWebSocketSession[id=0, uri=/myt/websocket]
java.io.IOException: java.io.IOException: Unable to wrap data, invalid status [CLOSED]
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendMessageBlock(WsRemoteEndpointImplBase.java:315)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendMessageBlock(WsRemoteEndpointImplBase.java:258)
    at org.apache.tomcat.websocket.WsSession.sendCloseMessage(WsSession.java:606)
    at org.apache.tomcat.websocket.WsSession.doClose(WsSession.java:494)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendMessageBlock(WsRemoteEndpointImplBase.java:313)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendMessageBlock(WsRemoteEndpointImplBase.java:250)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendPartialString(WsRemoteEndpointImplBase.java:223)
    at org.apache.tomcat.websocket.WsRemoteEndpointBasic.sendText(WsRemoteEndpointBasic.java:49)
    at org.springframework.web.socket.adapter.standard.StandardWebSocketSession.sendTextMessage(StandardWebSocketSession.java:197)
    at org.springframework.web.socket.adapter.AbstractWebSocketSession.sendMessage(AbstractWebSocketSession.java:104)
    at com.ebs.mytreasury.frontend.websocket.FrontendWebSocketHandler.send(FrontendWebSocketHandler.java:180)
    at com.ebs.mytreasury.frontend.websocket.FrontendWebSocketHandler.send(FrontendWebSocketHandler.java:24)
    at com.ebs.mytreasury.frontend.GenericMessageEndpoint.sendMessage(GenericMessageEndpoint.java:72)
    at com.ebs.mytreasury.frontend.GenericMessageEndpoint.send(GenericMessageEndpoint.java:57)
    at com.mytreasury.services.backend.MessageSender.process(MessageSender.java:24)
    at com.mytreasury.services.backend.AbstractTopicMessageHandler.handle(AbstractTopicMessageHandler.java:47)
    at com.mytreasury.services.backend.AbstractTopicMessageHandler.handle(AbstractTopicMessageHandler.java:52)
    at com.mytreasury.services.backend.AbstractTopicMessageHandler.handle(AbstractTopicMessageHandler.java:52)
    at com.mytreasury.services.backend.AbstractTopicMessageHandler.handle(AbstractTopicMessageHandler.java:52)
    at com.mytreasury.services.backend.AbstractTopicMessageHandler.handle(AbstractTopicMessageHandler.java:52)
    at com.mytreasury.services.backend.AbstractTopicMessageHandler.handle(AbstractTopicMessageHandler.java:52)
    at com.mytreasury.services.backend.AbstractTopicMessageHandler.handle(AbstractTopicMessageHandler.java:52)
    at com.mytreasury.services.backend.AbstractTopicMessageHandler.handle(AbstractTopicMessageHandler.java:52)
    at com.mytreasury.services.backend.AbstractTopicMessageHandler.handle(AbstractTopicMessageHandler.java:52)
    at com.ebs.mytreasury.frontend.message.handler.ResponseGenericMessageHandler.handle(ResponseGenericMessageHandler.java:52)
    at com.ebs.mytreasury.frontend.socket.EventPublisherSocketHandler.handle(EventPublisherSocketHandler.java:96)
    at com.ebs.mytreasury.frontend.socket.EventPublisherSocketHandler.run(EventPublisherSocketHandler.java:135)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: Unable to wrap data, invalid status [CLOSED]
    at org.apache.tomcat.util.net.SecureNioChannel.write(SecureNioChannel.java:647)
    at org.apache.tomcat.util.net.NioBlockingSelector.write(NioBlockingSelector.java:101)
    at org.apache.tomcat.util.net.NioSelectorPool.write(NioSelectorPool.java:157)
    at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.doWrite(NioEndpoint.java:1241)
    at org.apache.tomcat.util.net.SocketWrapperBase.doWrite(SocketWrapperBase.java:670)
    at org.apache.tomcat.util.net.SocketWrapperBase.flushBlocking(SocketWrapperBase.java:607)
    at org.apache.tomcat.util.net.SocketWrapperBase.flush(SocketWrapperBase.java:597)
    at org.apache.tomcat.websocket.server.WsRemoteEndpointImplServer.doWrite(WsRemoteEndpointImplServer.java:95)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.writeMessagePart(WsRemoteEndpointImplBase.java:494)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendMessageBlock(WsRemoteEndpointImplBase.java:309)
    ... 29 common frames omitted
[https-jsse-nio-8443-exec-3] [FrontendWebSocketHandler] Transport error. Session: StandardWebSocketSession[id=0, uri=/myt/websocket]
javax.net.ssl.SSLException: bad record MAC
    at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
    at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1728)
    at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:981)
    at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:907)
    at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:781)
    at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
    at org.apache.tomcat.util.net.SecureNioChannel.read(SecureNioChannel.java:563)
    at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1222)
    at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1195)
    at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1168)
    at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:62)
    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:53)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:789)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1437)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javax.crypto.BadPaddingException: bad record MAC
    at sun.security.ssl.EngineInputRecord.decrypt(EngineInputRecord.java:238)
    at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:974)
    ... 18 common frames omitted
[pool-18-thread-3] [FrontendWebSocketHandler] Connection closed. Session: StandardWebSocketSession[id=0, uri=/myt/websocket], CloseStatus: CloseStatus[code=1006, reason=Unable to wrap data, invalid status [CLOSED]];
[pool-18-thread-2] [GenericMessageEndpoint] Exception while sending message to the endpoint: java.lang.IllegalStateException: The remote endpoint was in state [TEXT_PARTIAL_WRITING] which is an invalid state for called method,
[pool-18-thread-1] [GenericMessageEndpoint] Exception while sending message to the endpoint: java.lang.IllegalStateException: The remote endpoint was in state [TEXT_PARTIAL_WRITING] which is an invalid state for called method, 
[pool-18-thread-3] [GenericMessageEndpoint] Exception while sending message to the endpoint: java.io.IOException: java.io.IOException: Unable to wrap data, invalid status [CLOSED],