Netty несколько протоколов на одном порте?

Мне нужно было бы реализовать HTTP-сервер, на котором определенный URI позволяет перейти на соединение WebSocket. Стандартный класс обработчика WebSocket следующий:

https://netty.io/4.0/xref/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.html

В JavaDoc упоминается, что класс предназначен только для поддержки WebSockets, и если кто-то хочет также поддерживать HTTP-запросы в том же сокете, то следует обратиться к следующему примеру:

https://netty.io/4.0/xref/io/netty/example/http/websocketx/server/WebSocketServer.html

Однако в приведенном выше примере фактически используется класс WebSocketServerProtocolHandler ... Есть ли актуальный пример того, как это сделать?

2 ответа

Чтобы поддерживать как необработанные протоколы HTTP, так и протоколы WebSocket , вам необходимо реализовать настраиваемый io.netty.channel.ChannelInitializer где вы бы вставили и WebSocketServerProtocolHandler(вместе с необходимыми обработчиками кодирования и декодирования) для поддержки обновления протокола WebSocket на настраиваемом uri:

      public class ServerInitializer extends ChannelInitializer<SocketChannel> {

    private static final String WEBSOCKET_PATH = "/ws";

    private final SslContext sslCtx;

    public WebSocketServerInitializer(SslContext sslCtx) {
        this.sslCtx = sslCtx;
    }

    @Override
    public void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (sslCtx != null) {
            pipeline.addLast(sslCtx.newHandler(ch.alloc()));
        }
        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new HttpObjectAggregator(65536));
        pipeline.addLast(new HttpRequestHandler(WEBSOCKET_PATH));
        pipeline.addLast(new WebSocketServerCompressionHandler());
        pipeline.addLast(new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true));
        pipeline.addLast(new WebSocketIndexPageHandler(WEBSOCKET_PATH));
        pipeline.addLast(new WebSocketFrameHandler());
    }
}

Вот пример того, как HttpRequestHandler будет выглядеть так:

      public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    private final String websocketUri;

    public HttpRequestHandler(String wsUri) {
        websockeUri = wsUri;
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        if (this. websocketUri.equalsIgnoreCase(request.getUri())) { // if the request uri matches the web socket path, we forward to next handler which will handle the upgrade handshake
            ctx.fireChannelRead(request.retain()); // we need to increment the reference count to retain the ByteBuf for upcoming processing
        } else {
            // Otherwise, process your HTTP request and send the flush the response
            HttpResponse response = new DefaultHttpResponse(
                request.getProtocolVersion(), HttpResponseStatus.OK);
            response.headers().set(
                HttpHeaders.Names.CONTENT_TYPE,
                "text/html; charset=UTF-8");
            ctx.write(response);
            ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
            future.addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
        throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

Вот реализация WebSocketHandler отображение текста фрейма в верхнем регистре:

      public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        // If the WebSocket handshake was successful, we remove the HttpRequestHandler from the pipeline as we are no more supporting raw HTTP requests
        if (evt == WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE) {
            ctx.pipeline().remove(HttpRequestHandler.class);
        } else {
            // otherwise forward to next handler
            super.userEventTriggered(ctx, evt);
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        if (frame instanceof TextWebSocketFrame) {
            // Send the uppercase string back.
            String request = ((TextWebSocketFrame) frame).text();
            ctx.channel().writeAndFlush(new TextWebSocketFrame(request.toUpperCase(Locale.US)));
        } else {
            String message = "unsupported frame type: " + frame.getClass().getName();
            throw new UnsupportedOperationException(message);
        }
    }
}

На практике мы должны использовать независимые порты для разных протоколов. Когда http обновляется до WebSocket, мы вообще не можем отправлять http msg, потому что байты msg в http и WebSocket разные.

Другие вопросы по тегам