netty系列之:分離websocket處理器
簡介
在上一篇文章中,我們使用了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
本文已收錄於 //www.flydean.com/24-netty-websocket-server2/
最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!