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 разные.