【问题标题】:Netty multiple protocols on the same port?Netty 多个协议在同一个端口上?
【发布时间】:2021-10-14 00:29:28
【问题描述】:

我需要实现一个 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 类...是否有最新的示例说明如何执行此操作?

【问题讨论】:

    标签: http websocket netty netty4


    【解决方案1】:

    为了同时支持原始 HTTPWebSocket 协议,您需要实现自定义 io.netty.channel.ChannelInitializer,在其中插入 HttpRequestHandler 和 @987654323 @(以及所需的编码和解码处理程序)以支持自定义 uri 上的 WebSocket 协议升级:

    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);
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      实际上,我们应该为不同的协议使用独立的端口。当http升级到WebSocket时,我们根本无法发送http msg,因为http和WebSocket中的msg字节不同。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-04-28
        • 1970-01-01
        • 1970-01-01
        • 2012-08-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多