简介websocket是一个优秀的协议,它基于TCP并兼容HTTP网络协议。通过Websocket,我们可以实现客户端与服务端的即时通信,避免了客户端多次轮询带来的性能损失。既然websocket这么好用,那么在netty中如何使用websocket呢?netty中的websocket虽然是一个完全不同于HTTP协议的独立协议,但是在netty中它仍然是放在http包中的。让我们回忆一下netty中对各种协议的支持。如果要支持此协议,则必须使用解码器和编码器来对协议进行编码和解码。将传输的数据从ByteBuf转换为协议类型,或者将协议类型转换为ByteBuf。这是netty工作的核心原理,也是后续自定义netty扩展的基础。那么网络套接字呢?websocket的版本WebSocket作为一个协议,自然不是凭空而来的。经过不断的发展,才有了今天的WebSocket协议。webSocket具体的发展历程我们就不深究了。我们先来看看netty提供的各种WebSocket版本。在WebSocketVersion类中,我们可以看到:UNKNOWN(AsciiString.cached(StringUtil.EMPTY_STRING)),V00(AsciiString.cached("0")),V07(AsciiString.cached("7")),V08(AsciiString.cached("8")),V13(AsciiString.cached("13"));WebSocketVersion是一个枚举类型,它定义了4个版本的websocket。除了UNKNOWN,我们可以看到websocket的版本有0、7、8、13这几个。FrameDecoder和FrameEncoder我们知道websocket消息是通过frame来传输的,因为不同的websocket版本会影响frame的不同格式。SoweneeddifferentFrameDecoderandFrameEncodertoconvertbetweenWebSocketFrameandByteBuf.既然websocket有四个版本,那么相对应的就有4个版本的decoder和encoder:WebSocket00FrameDecoderWebSocket00FrameEncoderWebSocket07FrameDecoderWebSocket07FrameEncoderWebSocket08FrameDecoderWebSocket08FrameEncoderWebSocket13FrameDecoderWebSocket13FrameEncoder至于每个版本之间的frame有什么区别,我们这里就不细讲了,感兴趣的朋友可以关注我的Followuparticle.Friendswhoarefamiliarwithnettyshouldknowthatboththeencoderandthedecoderareusedtoconvertmessagesinthechannel.Sowhatisthesupportforwebsocketinnetty?WebSocketServerHandshakernettyprovidesaWebSocketServerHandshakerclasstounifytheuseofencoderanddecoder.nettyprovidesafactoryclassWebSocketServerHandshakerFactorytoreturndifferentWebSocketServerHandshakeraccordingtothewebsocketversionoftheclientrequestheader.publicWebSocketServerHandshakernewHandshaker(HttpRequestreq){CharSequenceversion=req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_VERSION);if(version!=null){if(version.equals(WebSocketVersion.V13.toHttpHeaderValue())){//有线协议的版本13-RFC6455(hybi规范草案的版本17)。返回新的WebSocketServerHandshaker13(webSocketURL、子协议、decoderConfig);}elseif(version.equals(WebSocketVersion.V08.toHttpHeaderValue())){//有线协议的版本8-hybi规范草案的版本10。返回新的WebSocketServerHandshaker08(webSocketURL、子协议、decoderConfig);}elseif(version.equals(WebSocketVersion.V07.toHttpHeaderValue())){//有线协议的版本8-hybi规范草案的版本07在。返回新的WebSocketServerHandshaker07(webSocketURL、子协议、decoderConfig);}else{返回空值;}}else{//假设版本00没有指定版本头returnnewWebSocketServerHandshaker00(webSocketURL,desubcoderConfig)同样,我们可以看到netty也为websocket定义了4个不同的WebSocketServerHandshakers。handleshake方法在WebSocketServerHandshaker中定义,通过传入channel,并为其添加编码器和解码器wsencoder",newWebSocketEncoder());p.addBefore(ctx.name(),"wsdecoder",newWebsocketDecoder());而新增的两个newWebSocketEncoder和newWebsocketDecoder分别定义在WebSocketServerHandshaker的具体实现中。WebSocketFrame的所有ecode和decode都在WebSocketFrame和ByteBuf中转换。WebSocketFrame继承自DefaultByteBufHolder,也就是说它是一个ByteBuf容器。除了保存ByteBuf外,它还有两个额外的属性,finalFragment和rsv。finalFragment表示该帧是否为最后一帧。对于数据量大的消息,消息将被拆分成不同的帧。这个属性特别有用。我们再看一下websocket协议报文的格式:012301234567890123456789012345678901+-+-+-+-+--------+-+------------+----------------------------+|F|R|R|R|操作码|M|有效载荷长度|扩展有效载荷长度||我|S|S|S|(4)|一个|(7)|(16/64)||N|V|V|V||S||(如果负载len==126/127)|||1|2|3||K||+-+-+-+-+------+-+------------+----------------+|扩展有效负载长度继续,如果有效负载len==127|+---------------+---------------------------+||屏蔽密钥,如果MASK设置为1|+--------------------------------+-----------------------------+|掩码键(续)|有效载荷数据|+-----------------------------------------------+:有效载荷数据继续...:+---------------------------------+|有效负载数据继续...|+--------------------------------------------------------------+rsv表示消息中的扩展字段,即RSV1,另外RSV2和RSV3是ByteBuf的一些基本操作。WebSocketFrame是一个抽象类,它的具体实现类如下:BinaryWebSocketFrameCloseWebSocketFrameContinuationWebSocketFramePingWebSocketFramePongWebSocketFrameTextWebSocketFrameBinaryWebSocketFrame和TextWebSocketFrame很好理解,代表了两种消息传输方式。CloseWebSocketFrame是一个表示关闭连接的框架。ContinuationWebSocketFrame表示消息中多个帧的表示。PingWebSocketFrame和PongWebSocketFrame是两个特殊的框架,它们主要用于服务端和客户端检测。这些帧与Websocket的消息类型一一对应。了解websocket的消息类型和对应的frame类是很有帮助的。netty中websocket的使用讲了这么多websocket的原理和实现类,接下来就是实战了。本例中,我们使用netty创建一个websocket服务器,然后使用浏览器客户端访问服务器。创建一个websocket服务器的过程和一个普通的netty服务器没什么区别。只是在ChannelPipeline中,需要添加一个自定义的WebSocketServerHandler:pipeline.addLast(newWebSocketServerHandler());这个WebSocketServerHandler需要做什么?它需要处理普通的HTTP请求和webSocket请求。这两个请求可以通过接收到的msg类型的不同来判断:publicvoidchannelRead0(ChannelHandlerContextctx,Objectmsg)throwsIOException{//根据消息类型处理两个不同的消息if(msginstanceofFullHttpRequest){handleHttpRequest(ctx,(FullHttpRequest)消息);}elseif(msginstanceofWebSocketFrame){handleWebSocketFrame(ctx,(WebSocketFrame)msg??);}}客户端建立websocket连接前,需要借用当前通道并启动handleshake://websocket握手WebSocketServerHandshakerFactorywsFactory=newWebSocketServerHandshakerFactory(getWebSocketLocation(req),null,true,5*1024*1024);握手器=wsFactory.newHandshaker(req);if(handshaker==null){WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());}else{handshaker.handshake(ctx.channel(),req);}我们拿到handshaker之后,就可以处理后续的WebSocketFrame了:privatevoidhandleWebSocketFrame(ChannelHandlerContextctx,WebSocketFrameframe){//处理各种websocket帧信息if(frameinstanceofCloseWebSocketFrame){handshaker.close(ctx,(CloseWebSocketFrame)frame.retain());返回;}if(frameinstanceofPingWebSocketFrame){ctx.write(newPongWebSocketFrame(frame.content().retain()));返回;}if(frameinstanceofTextWebSocketFrame){//直接返回ctx.write(frame.retain());返回;}if(frameinstanceofBinaryWebSocketFrame){//直接返回ctx.write(frame.retain());}}这里只是机械地返回消息,大家可以根据自己的业务逻辑来分析消息有了服务器端,客户端应该如何连接?很简单,先构造一个WebSocket对象,然后处理各种回调:socket=newWebSocket("ws://127.0.0.1:8000/websocket");socket.onmessage=function(event){}socket.onopen=function(event){};socket.onclose=function(event){};综上所述,以上就是使用netty搭建websocketserver的完整过程。本文的服务器可以同时处理普通的HTTP请求和webSocket请求,但是有点复杂。有没有更简单的方法呢?敬请关注。本文示例可参考:learn-netty4本文已收录于http://www.flydean.com/23-netty-websocket-server/最流行的解读,最深刻的干货,最简洁的教程,很多你不知道的小技巧等你来发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!
