在 tomcat 中无法通过 websocket 发送二进制消息,但在 glassfish 中有效。在 tomcat 中失败并出现 IllegalArgumentException

Unable to send binary message through websocket in tomcat, but works in glassfish. Fails in tomcat with IllegalArgumentException

我正在尝试通过 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();
        }
    }

看起来您需要检查 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);