0

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

4

2 回答 2

1

为了同时支持原始HTTPWebSocket协议,您需要实现一个自定义io.netty.channel.ChannelInitializer,您将在其中插入一个HttpRequestHandler和 a WebSocketServerProtocolHandler(以及所需的编码和解码处理程序)以支持自定义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);
        }
    }
}
于 2021-08-10T14:29:37.633 回答
0

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

于 2021-09-03T05:50:22.587 回答