Java WebSockets:远程端点处于 [TEXT_FULL_WRITING] 状态

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22257079/
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-08-13 14:34:44  来源:igfitidea点击:

Java WebSockets: The remote endpoint was in state [TEXT_FULL_WRITING]

javatomcatwebsocketjsr356

提问by walv

I am trying to implement some application based on websockets which will communicate with JS clients quite intensively.

我正在尝试实现一些基于 websockets 的应用程序,它将与 JS 客户端进行非常密集的通信。

The code to send the message is quite primitive:

发送消息的代码非常原始:

synchronized (session) {
    if (session.isOpen()) {
        session.getBasicRemote().sendText(message);
    }
}

For rare sending it works just fine, but when few threads are trying to send some messages by the same session (socket), next exception is thrown (please note that it is not multithreading issue because code block is synchronized by session):

对于罕见的发送它工作得很好,但是当很少有线程尝试通过同一个会话(套接字)发送一些消息时,会抛出下一个异常(请注意这不是多线程问题,因为代码块是由会话同步的):

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:1015)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.textStart(WsRemoteEndpointImplBase.java:978)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendString(WsRemoteEndpointImplBase.java:161)
at org.apache.tomcat.websocket.WsRemoteEndpointBasic.sendText(WsRemoteEndpointBasic.java:37)

Google is not rich of such type of exceptions at the moment and after beating few hours on this issue, still no solution.

谷歌目前没有丰富的此类异常,在这个问题上打了几个小时后,仍然没有解决方案。

Java 7.0.21, tested on Tomcat 7.0.52 and Tomcat 8.0.3.

Java 7.0.21,在 Tomcat 7.0.52 和 Tomcat 8.0.3 上测试。

Any answer is highly appreciated! Thanks in advance.

任何答案都非常感谢!提前致谢。

UPDATE 3/11/2014: I tested my application with Jetty 9.1 and this exception didn't occur. I assume that this is Tomcat implementation bug.

2014 年 3 月 11 日更新:我使用 Jetty 9.1 测试了我的应用程序,但没有发生此异常。我假设这是 Tomcat 实现错误。

采纳答案by walv

OK, this is not a Tomcat issue but my fault.

好的,这不是 Tomcat 的问题,而是我的错。

My onMessage function returned a string, what means that I echoed the message back. As result, those part of code was not synced.

我的 onMessage 函数返回了一个字符串,这意味着我回显了消息。结果,那部分代码没有同步。

Bad:

坏的:

@OnMessage
public String onMessage(String message, Session session) {
   ...
   return message;
}

Good:

好的:

@OnMessage
public void onMessage(String message, Session session) {
   ...
}

回答by bulletProofCat

I found this: https://bz.apache.org/bugzilla/show_bug.cgi?id=56026

我发现了这个:https: //bz.apache.org/bugzilla/show_bug.cgi?id=56026

seems tomcat has done something unexpected, as a workaround you must synchronize all session.sendxxx calls no matter whether it's async.

似乎 tomcat 做了一些意想不到的事情,作为一种解决方法,您必须同步所有 session.sendxxx 调用,无论它是否是异步的。

回答by xer21

I just ran into this issue today, the accepted answer wasn't the resolution for me. I tried synchronizing every call to the remote endpoint that was in my code, which was only 4 instances. That did not fix it either. I also tried updating to the latest tomcat version which at this time was 9.0.24 which did not fix it.

我今天刚遇到这个问题,接受的答案不是我的解决方案。我尝试将每次调用同步到我的代码中的远程端点,它只有 4 个实例。那也没有解决它。我还尝试更新到最新的 tomcat 版本,此时 9.0.24 没有修复它。

It seams the source of my issue is that in a single websocket message request that came in, I happen to send two DIFFERENT messages (on purpose) during the request. I verified both sendText calls were properly synchronized, they were getting called about 0.001 milliseconds a part or less in different blocks.

我的问题的根源在于,在传入的单个 websocket 消息请求中,我碰巧在请求期间发送了两个不同的消息(故意)。我验证了两个 sendText 调用都正确同步,它们在不同块中被调用大约 0.001 毫秒或更少。

My solution that I worked up real quick was to use the Async version of the remote endpoint, and just make sure the last msg's future was done by the time the next msg is requested to be sent. I wasn't thrilled about this but it did fix the issue... here is the class I wrote and I just now reference this object anytime I want to send something over the websocket without requiring the code in a sync block, since the send* methods on this class are already synchronized. Hope this helps someone down the line.

我真正快速制定的解决方案是使用远程端点的异步版本,并确保在请求发送下一个 msg 时完成最后一个 msg 的未来。我对此并不感到兴奋,但它确实解决了这个问题......这是我写的类,我现在引用这个对象,只要我想通过 websocket 发送一些东西而不需要同步块中的代码,因为发送* 此类上的方法已经同步。希望这可以帮助某人下线。

NOTE: I did not synchronize anything other than the send* so not sure if Ping/Pong will have the same issue or not, I've never used those.

注意:我没有同步发送*以外的任何东西,所以不确定 Ping/Pong 是否会遇到同样的问题,我从未使用过这些。

public class WebSocketEndpointAsync implements RemoteEndpoint.Async {
    private final Session _session;
    private final Async _ep;
    private Future<Void> _lastFuture = null;

    public WebSocketEndpointAsync(Session session, Async ep)
    {
        _session = session;
        _ep = ep;
    }

    @Override public long getSendTimeout() { return _ep.getSendTimeout(); }
    @Override public void setSendTimeout(long timeout) { _ep.setSendTimeout(timeout); }
    @Override public void setBatchingAllowed(boolean allowed) throws IOException { _ep.setBatchingAllowed(allowed); }
    @Override public boolean getBatchingAllowed() { return _ep.getBatchingAllowed(); }
    @Override public void flushBatch() throws IOException { _ep.flushBatch(); }
    @Override public void sendPing(ByteBuffer byteBuffer) throws IOException, IllegalArgumentException { _ep.sendPing(byteBuffer); }
    @Override public void sendPong(ByteBuffer byteBuffer) throws IOException, IllegalArgumentException { _ep.sendPong(byteBuffer); }

    @Override public void sendText(String s, SendHandler sendHandler) { throw new UnsupportedOperationException(); }
    @Override public void sendBinary(ByteBuffer byteBuffer, SendHandler sendHandler) { throw new UnsupportedOperationException(); }
    @Override public void sendObject(Object o, SendHandler sendHandler) { throw new UnsupportedOperationException(); }

    protected synchronized void checkLastSendComplete() {
        if (_lastFuture != null) {
            try {
                if (!_lastFuture.isDone()) {
                    // Only one write to the websocket can happen at a time, so we need to make sure the last one completed
                    // else we get ...
                    // java.lang.IllegalStateException: The remote endpoint was in state [TEXT_FULL_WRITING] which is an invalid state for called method
                    do { Thread.sleep(1); }
                    while (!_lastFuture.isDone());
                }
                // Get the result to ensure
                var ignore = _lastFuture.get();
            }
            catch (InterruptedException ie) { }
            catch (ExecutionException ee) { }
        }
    }
    @Override
    public synchronized Future<Void> sendText(String text) {
        checkLastSendComplete();
        return (_lastFuture = _ep.sendText(text));
    }

    @Override
    public synchronized Future<Void> sendBinary(ByteBuffer byteBuffer) {
        checkLastSendComplete();
        return (_lastFuture = _ep.sendBinary(byteBuffer));
    }

    @Override
    public synchronized Future<Void> sendObject(Object obj) {
        checkLastSendComplete();
        return (_lastFuture = _ep.sendObject(obj));
    }
}