无法通过 tomcat 中的 websocket 发送二进制消息,但可以在 glassfish 中使用。使用 IllegalArgumentException 在 tomcat 中失败

Posted

技术标签:

【中文标题】无法通过 tomcat 中的 websocket 发送二进制消息,但可以在 glassfish 中使用。使用 IllegalArgumentException 在 tomcat 中失败【英文标题】:Unable to send binary message through websocket in tomcat, but works in glassfish. Fails in tomcat with IllegalArgumentException 【发布时间】:2015-01-22 04:13:25 【问题描述】:

我正在尝试通过 websocket 发送使用协议缓冲区编码的二进制消息。我能够使用 Glassfish 4.0 成功发送。但相同的代码在 tomcat 8 中失败,但有以下异常。

java.lang.IllegalArgumentException
at java.nio.Buffer.limit(Buffer.java:267)
at org.apache.tomcat.websocket.PerMessageDeflate.sendMessagePart(PerMessageDeflate.java:368)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessage(WsRemoteEndpointImplBase.java:297)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessageBlock(WsRemoteEndpointImplBase.java:270)
at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendBytes(WsRemoteEndpointImplBase.java:132)
at org.apache.tomcat.websocket.WsRemoteEndpointBasic.sendBinary(WsRemoteEndpointBasic.java:43)
at com.trsim.sim.endpoint.Whiteboard.broadcastSnapshot(Whiteboard.java:164)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.websocket.pojo.PojoMessageHandlerWholeBase.onMessage(PojoMessageHandlerWholeBase.java:80)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageBinary(WsFrameBase.java:586)
at org.apache.tomcat.websocket.WsFrameBase.processDataBinary(WsFrameBase.java:543)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:295)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:130)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:60)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:203)
at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:194)
at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:96)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:654)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:744)

以下是我处理二进制消息并将其发送回的 java 实现

 @OnMessage
        public void broadcastSnapshot(ByteBuffer data, Session session) throws IOException 
            //I am able to parse the incoming binary header data
            Header incomingHeader = Header.parseFrom(data.array());
            Header header = Header.newBuilder().setCallback("2").setDecodeusing("MyClasstodecode").build();
            //I am able to parse the incoming data
            AddressBook incomingAddressBook = AddressBook.parseFrom(data.array());


        //Creating Ouput messages
        Person john =
                  Person.newBuilder()
                    .setId(1234)
                    .setName("John Doe")
                    .setEmail("jdoe@example.com")
                    .addPhone(
                      Person.PhoneNumber.newBuilder()
                        .setNumber("555-4321")
                        .setType(Person.PhoneType.HOME))
                    .build();
        Person keith =
                Person.newBuilder().setId(1234).setName("John Doe").setEmail("jdoe@example.com").addPhone(Person.PhoneNumber.newBuilder().setNumber("555-4321").setType(Person.PhoneType.HOME)).build();
        AddressBook.Builder addressBook = AddressBook.newBuilder();
        addressBook.addPerson(john);
        addressBook.addPerson(keith);


        //Converting output message to byte array using Protocol buffers
        byte[] headerArray = header.toByteArray();
        byte[] byteBuffer = addressBook.build().toByteArray();


        ByteBuffer outputBuffer = ByteBuffer.allocate(byteBuffer.length + headerArray.length );
        outputBuffer.put(headerArray,0,headerArray.length);
        outputBuffer.put(byteBuffer,0,byteBuffer.length);

        try
            //Send the received message first -- fails in tomcat but works in glassfish 4
            ByteBuffer buffer = ByteBuffer.allocate(incomingAddressBook.toByteArray().length).put(incomingAddressBook.toByteArray());
            session.getBasicRemote().sendBinary(buffer);

            //Send the response -- fails in tomcat.. But works in glassfish 4
            session.getBasicRemote().sendBinary(outputBuffer);
        catch(Exception e)
            e.printStackTrace();
        
    

【问题讨论】:

【参考方案1】:

看来您需要检查 Tomcat 服务器配置的缓冲区大小。 二进制消息的默认缓冲区大小为 8192 字节。见WebSocket Howto

incomingAddressBook.toByteArray().length 的值是多少?

在您的 web.xml 中尝试以下操作,

<context-param>
     <param-name>org.apache.tomcat.websocket.textBufferSize</param-name>
     <param-value>32768</param-value>
</context-param>
<context-param>
     <param-name>org.apache.tomcat.websocket.binaryBufferSize</param-name>
     <param-value>32768</param-value>
</context-param>

另外,使用下面的。,

ByteBuffer buffer = ByteBuffer.allocate(incomingAddressBook.toByteArray().length).put(incomingAddressBook.toByteArray());
buffer.flip(); //before sending message
session.getBasicRemote().sendBinary(buffer);

【讨论】:

我已经有了 org.apache.tomcat.websocket.binaryBufferSize163840上下文参数> 我尝试将收到的消息发回,但没有成功 尝试使用 buffer.flip();在发送消息之前

以上是关于无法通过 tomcat 中的 websocket 发送二进制消息,但可以在 glassfish 中使用。使用 IllegalArgumentException 在 tomcat 中失败的主要内容,如果未能解决你的问题,请参考以下文章

无法在tomcat 8上打开与websocket的连接

风险通告 Tomcat WebSocket 拒绝服务(EXP公开)

TomCat与WebSocket

基于tomcat 7.0.68 的websocket 实现,及通过 HttpSessionId 实现websocket session 共享

基于tomcat 7.0.68 的websocket 实现,及通过 HttpSessionId 实现websocket session 共享

基于tomcat 7.0.68 的websocket 实现,及通过 HttpSessionId 实现websocket session 共享