无法通过 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);
【讨论】:
我已经有了以上是关于无法通过 tomcat 中的 websocket 发送二进制消息,但可以在 glassfish 中使用。使用 IllegalArgumentException 在 tomcat 中失败的主要内容,如果未能解决你的问题,请参考以下文章
风险通告 Tomcat WebSocket 拒绝服务(EXP公开)
基于tomcat 7.0.68 的websocket 实现,及通过 HttpSessionId 实现websocket session 共享
基于tomcat 7.0.68 的websocket 实现,及通过 HttpSessionId 实现websocket session 共享
基于tomcat 7.0.68 的websocket 实现,及通过 HttpSessionId 实现websocket session 共享