簡介

在上一篇文章中,我們使用了netty構建了可以處理websocket協議的伺服器,在這個伺服器中,我們構建了特製的handler用來處理HTTP或者websocket請求。

在一個handler中處理兩種不同的請求,對於某些有程式碼潔癖的人可能忍受不了。那麼,有沒有可能將普通的HTTP請求和websocket請求使用不同的handler來進行處理呢?答案是肯定的。

netty的訊息處理

我們知道netty中所有的訊息處理都是通過handler來實現的,為了方便起見,netty提供了一個簡單的訊息處理類SimpleChannelInboundHandler,大家通過繼承它來重寫channelRead0方法即可:

protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;

我們再看一下SimpleChannelInboundHandler的定義:

public abstract class SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter

可以看到SimpleChannelInboundHandler本身是帶有泛型I的,而這個I就是我們要探討的方向。

如果我們要使用這個handler來處理所有的訊息,那麼可以將I取值為Object。

如果我們只需要處理String訊息,那麼可以這樣:

       public class StringHandler extends
SimpleChannelInboundHandler<String> { @Override
protected void channelRead0(ChannelHandlerContext ctx, String message)
throws Exception {
System.out.println(message);
}
}

同樣的,如果要同時處理HTTP和WebSocket訊息,只需要將I設定為不同的型別即可。

對於WebSocketFrame,我們有:

public class Server2FrameHandler extends SimpleChannelInboundHandler<WebSocketFrame>

對於FullHttpRequest,我們有:

public class Server2HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest>

處理WebSocketFrame

對於WebSocketFrame訊息,從上一節我們知道它有6種類型,分別是:

BinaryWebSocketFrame
CloseWebSocketFrame
ContinuationWebSocketFrame
PingWebSocketFrame
PongWebSocketFrame
TextWebSocketFrame

其中真正包含內容的是TextWebSocketFrame和BinaryWebSocketFrame,這裡我們對TextWebSocketFrame進行專門處理:

    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {

        if (frame instanceof TextWebSocketFrame) {
// 將接收到的訊息轉換成為大寫
String request = ((TextWebSocketFrame) frame).text();
ctx.channel().writeAndFlush(new TextWebSocketFrame(request.toUpperCase(Locale.CHINA)));
} else {
String message = "不支援的Frame型別: " + frame.getClass().getName();
throw new UnsupportedOperationException(message);
}
}

處理HTTP

對於HTTP請求中的FullHttpRequest,我們就安裝正常的HTTP服務請求的處理流程來就行。

這裡不做過多闡述。

編碼和解碼器

等等,我們是不是忘記了什麼東西?對,那就是編碼和解碼器。

在上一節中,我們使用的是WebSocketServerHandshaker來對websocket訊息進行編碼和解碼。不過其實是放在我們自定義的hadler程式碼裡面的,使用起來略顯不優雅。

沒關係,netty為我們提供了一個WebSocketServerProtocolHandler類,專門負責websocket的編碼和解碼問題。

除了處理正常的websocket握手之外,WebSocketServerProtocolHandler類還為我們處理了Close, Ping, Pong這幾種通用的訊息型別。而我們只需要專注於真正的業務邏輯訊息即可,十分的方便。

對於剩下的Text或者Binary frame資料,會被交由pipline中的下一個handler進行處理。

其中Handshake有兩個狀態,分別是:

HANDSHAKE_COMPLETE 和 HANDSHAKE_TIMEOUT。

而HandshakeComplete又包含了requestUri,requestHeaders和selectedSubprotocol這三個方面的資訊。

最後,將WebSocketServerProtocolHandler加入到pipeline中,最終得到:

    public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new WebSocketServerCompressionHandler());
pipeline.addLast(new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true));
pipeline.addLast(new Server2HttpHandler());
pipeline.addLast(new Server2FrameHandler());
}

總結

一個分離了HTTP請求和webSocket請求的伺服器就完成了。簡單直觀才是一個程式設計師追求的世界!

本文的例子可以參考:learn-netty4

本文已收錄於 http://www.flydean.com/24-netty-websocket-server2/

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!