Netty4 websockets:如果我们尝试在websocket connect之后过快地发送消息,则从HttpObjectDecoder获取IllegalStateException

时间:2013-11-21 21:40:48

标签: websocket netty

更新了附加代码

使用Netty 4.0.12,如果我们尝试在连接websocket后立即发送消息,我们将从HttpObjectEncoder获取IllegalStateException(请参阅底部的异常)。如果我睡了1-2秒,那么一切都很好。

我认为这是因为我们没有正确处理ChannelFuture,但我相信我已经通过使用ChannelFuture上的以下逻辑等待未来完成来确定连接已完成之前我们尝试用它。

不幸的是,这并没有解决它。如果有人知道可能导致这种情况的原因,请告诉我。

提前致谢, 鲍勃

=====================

WEBSOCKET CREATION

public synchronized Channel createWebSocket(String id, NettyClientConnection connection) throws Exception
{
    URI serverUri = connection.getServerUri();
    final ClientHandler clientHandler = new ClientHandler(connection);

    this.getBootstrap().handler(new ChannelPipelineInitializer(serverUri, clientHandler));

    ChannelFuture future = this.getBootstrap().connect(serverUri.getHost(), serverUri.getPort());

    // verify connect has completed successfully
    NettyUtils.waitForChannelCompletion(future, "connecting websocket");

    Channel websocket = future.channel();

    return websocket;
}

PIPELINE INIT

public class ChannelPipelineInitializer extends ChannelInitializer<SocketChannel> 
{
    private URI _serverUri = null;
    protected URI getServerUri(){return this._serverUri;}

    private ClientHandler _clientHandler = null;
    protected ClientHandler getClientHandler(){return this._clientHandler;}

    public ChannelPipelineInitializer(URI serverUri, ClientHandler clientHandler)
    {
        this._serverUri = serverUri;
        this._clientHandler = clientHandler;
    }

    @Override
    public void initChannel(SocketChannel channel) throws Exception 
    {
        ChannelPipeline pipeline = channel.pipeline();

        boolean handleCloseFrames = false;
        WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker(this.getServerUri(), WebSocketVersion.V13, null, false, null);

        final WebSocketClientProtocolHandler websocketHandler = new WebSocketClientProtocolHandler(handshaker, handleCloseFrames);

        DefaultEventExecutorGroup nettyExecutor = new DefaultEventExecutorGroup(10);

        pipeline.addLast(PipelineConstants.HttpClientCodec, new HttpClientCodec());
        pipeline.addLast(PipelineConstants.HttpAggregator, new HttpObjectAggregator(65536));
        pipeline.addLast(PipelineConstants.WebSocketClientProtocolHandler, websocketHandler);
        pipeline.addLast(PipelineConstants.ThingworxMessageCodec, new ThingworxMessageCodec());

        // use netty executor to free up initial IO thread
        pipeline.addLast(nettyExecutor, this.getClientHandler());
    }
}

CHANNEL WAIT LOGIC

public static void waitForChannelCompletion(ChannelFuture future, String operationMessage) throws IOCompletionException
{
    future.awaitUninterruptibly();

    // Now we are sure the future is completed.
    if (future.isDone())
    {
        if (future.isCancelled())
        {
            String errorMsg = String.format("IO Operation has been cancelled [operation: %s]", operationMessage);
            throw new IOCompletionException(errorMsg);
        }
        else if (future.isSuccess() == false) 
        {
            String errorMsg = String.format("IO Operation failed [operation: %s]", operationMessage);
            throw new IOCompletionException(errorMsg, future.cause());
        }
    }
    else
    {
        // future should be done, otherwise there's a problem
        String errorMsg = String.format("IO Operation never completed [operation: %s]", operationMessage);
        throw new IOCompletionException(errorMsg);
    }
}

EXCEPTION STACK

2013-11-21 13:32:16.767-0500 [ERROR] [c.t.t.t.SendTask] [T: pool-2-thread-9] Client_9 Attempt to send message failed. java.lang.IllegalStateException: unexpected message type: UnpooledUnsafeDirectByteBuf
    at io.netty.handler.codec.http.HttpObjectEncoder.encode(HttpObjectEncoder.java:80) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.handler.codec.http.HttpClientCodec$Encoder.encode(HttpClientCodec.java:94) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:89) ~[netty-all-4.0.12.Final.jar:na] Wrapped by: io.netty.handler.codec.EncoderException: java.lang.IllegalStateException: unexpected message type: UnpooledUnsafeDirectByteBuf
    at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:107) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.channel.CombinedChannelDuplexHandler.write(CombinedChannelDuplexHandler.java:193) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.channel.DefaultChannelHandlerContext.invokeWrite(DefaultChannelHandlerContext.java:645) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.channel.DefaultChannelHandlerContext.write(DefaultChannelHandlerContext.java:699) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.channel.DefaultChannelHandlerContext.write(DefaultChannelHandlerContext.java:638) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:115) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.channel.DefaultChannelHandlerContext.invokeWrite(DefaultChannelHandlerContext.java:645) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.channel.DefaultChannelHandlerContext.write(DefaultChannelHandlerContext.java:699) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.channel.DefaultChannelHandlerContext.write(DefaultChannelHandlerContext.java:638) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:115) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.handler.codec.MessageToMessageCodec.write(MessageToMessageCodec.java:116) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.channel.DefaultChannelHandlerContext.invokeWrite(DefaultChannelHandlerContext.java:645) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.channel.DefaultChannelHandlerContext.access$2000(DefaultChannelHandlerContext.java:29) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.channel.DefaultChannelHandlerContext$WriteTask.run(DefaultChannelHandlerContext.java:906) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:354) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:353) ~[netty-all-4.0.12.Final.jar:na]
    at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:101) ~[netty-all-4.0.12.Final.jar:na]
    at java.lang.Thread.run(Thread.java:744) [na:1.7.0_45]

1 个答案:

答案 0 :(得分:0)

看起来正在发生的是这个

1)客户端发送升级请求

2)服务器向客户端发送升级响应,触发本地HANDSHAKE_COMPLETED事件

3)上游服务器处理程序听到事件,开始向客户端发送websocket框架

4)客户端收到升级响应(HTTP 101)

5)客户端在#4到达websocket握手之前收到第一个websocket框架

HttpClientCodec在处理websocket框架时抛出异常,导致websocket握手关闭连接

6)客户端进程升级响应并删除HttpClientCodec (oops,为时已晚)

我也看到4.0.14.Final,但是在管道前面有HttpClientCodec。

**更新**

这是固定的,顺便说一句:https://github.com/netty/netty/issues/2173