android - 在 Android 设备上运行时,Netty websocket 客户端从不发送 websocket 消息
问题描述
使用在 JDK6-8 下运行的现有 websocket 客户端并将其部署到 Android (API 19) 会使客户端无法将 websocket 帧发送到服务器。
发生正常的 http 升级并且存在 http 连接,但使用 ChannelHandlerContext.writeAndFlush() 发送的任何消息都不会通过线路(我已通过服务器上的 Wireshark 确认了这一点)。
// 从 IClientConnection 的实现
public void sendBinaryMessage(FastByteArrayOutputStream stream) throws Exception { this.lock();
try {
if (_logger.isTraceEnabled()) {
System.out.println("Sending binary Message:\n" + HexDump.hexDump(stream.toByteArray(), 20));
}
// recommended use when you already have an existing byte[] is to
// create the ByteBuf using Unpooled, not the PooledByteBufAllocator
ByteBuf buffer = Unpooled.wrappedBuffer(stream.getByteArray(), 0, stream.getSize());
BinaryWebSocketFrame binaryMessage = new BinaryWebSocketFrame(buffer);
String st = StringUtilities.toHexString(buffer.array());
_logger.debug("SENDING AUTH MESSAGE:" + st);
ChannelFuture promise = this.getWebSocket().writeAndFlush(binaryMessage);
promise.await();
_logger.debug("MESSAGE SENT");
} finally {
this.unlock();
}
}
日志输出
I/System.out: 14:24:18.605 [Client-EndpointMonitor-1] DEBUG io.netty.util.NetUtil - Loopback interface: lo (lo, ::1%1)
I/System.out: 14:24:18.615 [Client-EndpointMonitor-1] DEBUG io.netty.util.NetUtil - /proc/sys/net/core/somaxconn: 128
I/System.out: 14:24:18.738 [Client-EndpointMonitor-1] DEBUG io.netty.channel.DefaultChannelId - -Dio.netty.machineId: 90:68:c3:ff:fe:f1:ed:5c (auto-detected)
I/System.out: 14:24:18.751 [Client-EndpointMonitor-1] DEBUG io.netty.util.internal.ThreadLocalRandom - -Dio.netty.initialSeedUniquifier: 0x63ddb0bd5ba71d45
I/System.out: 14:24:18.835 [Client-EndpointMonitor-1] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: unpooled
I/System.out: 14:24:18.842 [Client-EndpointMonitor-1] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 0
I/System.out: 14:24:18.850 [Client-EndpointMonitor-1] DEBUG io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384
I/System.out: 14:24:19.127 [NettyClient-NIO-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.checkAccessible: true
D/com.thingworx.sdk.android.activity.ThingworxActivity: Waiting for initial connection...
I/System.out: 14:24:19.135 [NettyClient-NIO-1] DEBUG io.netty.buffer.AbstractByteBuf - -Dio.netty.buffer.checkBounds: true
I/System.out: 14:24:19.145 [NettyClient-NIO-1] DEBUG io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.netty.util.ResourceLeakDetector@2bea8c32
I/System.out: 14:24:19.595 [NettyClient-NIO-1] DEBUG com.thingworx.communications.client.connection.netty.NettyClientConnectionFactory - Adding compression handler to pipeline
I/System.out: 14:24:19.606 [NettyClient-NIO-1] DEBUG io.netty.handler.codec.compression.ZlibCodecFactory - -Dio.netty.noJdkZlibDecoder: true
I/System.out: 14:24:19.615 [NettyClient-NIO-1] DEBUG io.netty.handler.codec.compression.ZlibCodecFactory - -Dio.netty.noJdkZlibEncoder: false
D/com.thingworx.sdk.android.activity.ThingworxActivity: Waiting for initial connection...
I/System.out: 14:24:20.501 [NettyClient-NIO-1] DEBUG com.thingworx.communications.client.connection.netty.ThingworxClientConnectionHandler - WebSocket Channel is connected [is open: true]
D/com.thingworx.sdk.android.activity.ThingworxActivity: Waiting for initial connection...
I/System.out: 14:24:21.144 [NettyClient-NIO-1] DEBUG io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker13 - WebSocket version 13 client handshake key: AAX2bRU+jf63Fhyik0HAEg==, expected response: DOFv14sfAg0+tQcv2kuDguaF1Sw=
I/System.out: 14:24:21.190 [NettyClient-NIO-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 4096
I/System.out: 14:24:21.198 [NettyClient-NIO-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.maxSharedCapacityFactor: 2
I/System.out: 14:24:21.205 [NettyClient-NIO-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.linkCapacity: 16
I/System.out: 14:24:21.213 [NettyClient-NIO-1] DEBUG io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8
I/System.out: 14:24:21.830 [NettyClient-NIO-1] DEBUG com.thingworx.communications.client.connection.netty.ThingworxClientConnectionHandler - [ClientHandler: 911311818] Client websocket handshake is complete
I/System.out: 14:24:21.847 [Client-EndpointMonitor-1] INFO com.thingworx.communications.client.endpoints.ClientCommunicationEndpoint - Preparing new Connection Authentication Request: DispatchingClientEndpoint [id: 0, isConnected: false, open connections: 0, max connections: 1]
I/System.out: 14:24:21.864 [Client-EndpointMonitor-1] DEBUG com.thingworx.communications.common.endpoints.CommunicationEndpoint - Sending connection authentication message, waiting for response [sync key: 1, message: AuthRequestMessage [requestId: 1, endpointId: -1, sessionId: -1, method: AUTHREQUEST]]
D/com.thingworx.sdk.android.activity.ThingworxActivity: Waiting for initial connection...
D/com.thingworx.sdk.android.activity.ThingworxActivity: Waiting for initial connection...
I/System.out: 14:24:22.694 [Client-EndpointMonitor-1] DEBUG com.thingworx.communications.client.connection.netty.NettyClientConnection - SENDING AUTH MESSAGE:011400000001ffffffffffffffff0001066170704b65792461626630333663322d373030392d343436322d386139362d363033366565376562613533
D/com.thingworx.sdk.android.activity.ThingworxActivity: Waiting for initial connection...
I/Choreographer: Skipped 206 frames! The application may be doing too much work on its main thread.
I/System.out: 14:24:26.794 [Client-EndpointMonitor-1] DEBUG com.thingworx.communications.client.connection.netty.NettyClientConnection - MESSAGE SENT
请注意,MESSAGE SENT 是从 writeAndFlush() 返回 true 的承诺返回的
就预期结果而言,我希望二进制 websocket 消息能够通过网络发送出去,但它永远不会。
解决方案
我有一个答案。
事实证明,我的 ChannelFuture 承诺中出现了一个类加载异常。因为没有记录类加载异常,所以对我来说就像没有发送我的消息。
这是一个例外:
Caused by: io.netty.handler.codec.EncoderException: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/jcraft/jzlib/Deflater;
at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:107)
at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:716)
at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:708)
at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:791)
at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:701)
现在很清楚,必须禁用 websocket 压缩(至少在短期内)才能允许发送 websocket 流量。
我没有正确处理承诺返回状态。
下面是对允许我看到类加载异常的代码的修改。
public void waitForChannelCompletion(ChannelFuture future, String operationMessage) throws IOException {
future.awaitUninterruptibly();
// Now we are sure the future is completed.
if (future.isDone()) {
if (future.isCancelled()) {
String errorMsg = String.format("Netty IO Operation has been cancelled [operation: %s]", operationMessage);
throw new IOException(errorMsg);
} else if (future.isSuccess() == false) {
String errorMsg = String.format("Netty IO Operation failed [operation: %s]", operationMessage);
throw new IOException(errorMsg, future.cause());
}
} else {
// future should be done, otherwise there's a problem
String errorMsg = String.format("Netty IO Operation is not done [operation: %s]", operationMessage);
throw new IOException(errorMsg);
}
}
ChannelFuture promise = this.getWebSocket().writeAndFlush(binaryMessage);
waitForChannelCompletion(promise,"MESSAGE SENT");
希望这可以避免其他人遇到类似的问题。
推荐阅读
- javascript - 尝试通过 AJAX 提交 rails 表单时出现 400 错误
- c++ - 如何使用 cpp 在 unordered_map 中分配 lambda 函数?
- linq - 当我从 Azure 函数而不是控制台应用程序运行 LINQ 查询时失败
- apache-spark - 修复 Spark 结构化流中的检查点
- python - 如何按两个字段分组并按日期时间排序?
- mod-rewrite - RewriteRule 否定(NOT)不起作用
- ios - 如何持久化 tableview 行?
- python - 如何使数据类更好地与 __slots__ 一起工作?
- c# - 在 C# mysql 中读取 mediumblob
- node.js - 在客户端包含 nodejs js 模块