Netty 应用:WebSocket服务
考虑一个场景:如何实现网页版的聊天程序?
不同于Socket的聊天程序
在以往的http1.0,http1.1当中,是没法实现长连接的。 没出现Websocket
之前,采用的是一种轮询的方式,如Comet技术。
术语
实时Web
:利用技术和实践,使用户在信息的作者发布信息之后就能够立即收到信息,而不需要他们或者他们的软件周期性的检查信息源以获取更新。
Websocket
是重新设计的协议,目的是为Web上的双向数据传输问题提供一个可行的解决方案,使得客户端和服务器能在任意时刻传输消息,因此要求他们异步的处理消息回调(websocket作为html5的一部分,大部分浏览器已经支持
)。
下面的例子实现的功能:基于websocket协议,客户端能向服务端发送消息,服务端返回服务器的当前时间。
服务端实现
/** * Created by fubin on 2019/7/14. */ public class WebsocketServer { public static void main(String[] args) { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try{ ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,workerGroup) .channel(NioServerSocketChannel.class) //增加日志handler .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new WebsocketInitializer()); ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(8899)).sync(); channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } class WebsocketInitializer extends ChannelInitializer<SocketChannel>{ @Override protected void initChannel(SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); /** * 处理http请求 */ //http编解码 pipeline.addLast("httpCodec",new HttpServerCodec()); //以块的方式写的处理器 pipeline.addLast(new ChunkedWriteHandler()); //http消息聚合 -> FullHttpRequest或FullHttoResponse pipeline.addLast(new HttpObjectAggregator(8192)); /** * 处理websocket */ //websocket握手,控制帧Frames的处理 // ws://server:port/content_path(hellowebsocket) pipeline.addLast(new WebSocketServerProtocolHandler("/hellowebsocket")); pipeline.addLast(new TextWebsocketFrameHandler()); } } /** * 范型使用TextWebSocketFrame,Websocket传输使用Frame来传递的 */ class TextWebsocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame>{ @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { System.out.println("收到的消息:" + msg.text()); ctx.channel().writeAndFlush(new TextWebSocketFrame("server发送websocket frame ,服务器时间:" + LocalDateTime.now())); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { System.out.println("handlerAdded: channel的全局id - " + ctx.channel().id().asLongText()); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { System.out.println("handlerRemoved: channel的全局id - " + ctx.channel().id().asLongText()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("异常发生"); ctx.close(); } }
Netty服务端提供的websocket六种frame类型
客户端实现
websocket需要浏览器支持
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>websocket html</title> </head> <body> <script type="text/javascript"> var socket; if(window.WebSocket){ socket = new WebSocket("ws://localhost:8899/hellowebsocket"); //客户端回调,收到服务器端发送的消息 socket.onmessage = function (event) { var serverData = event.data; var responseTextVal = document.getElementById("responseText"); responseTextVal.value = responseTextVal.value + serverData; } //连接打开 socket.onopen = function (event) { var responseTextVal = document.getElementById("responseText"); responseTextVal.value = "连接打开..."; } socket.onclose = function (ev) { var responseTextVal = document.getElementById("responseText"); responseTextVal.value = "连接断开..."; } }else{ alert('浏览器不支持websocket!'); } function send(message) { if(!window.WebSocket){ return; } if(socket.readyState == WebSocket.OPEN){ socket.send(message); }else { alert("连接尚未开启!"); } } </script> <form onsubmit="return false"> <textarea name="message" style="width: 400px; height: 200px"></textarea> <input type="button" value="发送数据" onclick="send(this.form.message.value)"> <h1>服务端输出:</h1> <textarea id="responseText" style="width: 400px;height: 200px;"></textarea> <input type="button" onclick="javascript: document.getElementById('responseText').value = ''" value="清空内容"> </form> </body> </html>
websocket请求头
浏览器查看websocket帧
Netty提供的处理WebSocket服务的Handler WebSocketServerProtocolHandler
的API解释
这个处理程序为您运行websocket服务器做了所有繁重的工作。 它负责websocket握手
以及控制框架的处理(Close,Ping,Pong)
。 文本和二进制数据帧将传递给管道中的下一个处理程序(由您实现)进行处理。 有关用法,请参阅io.netty.example.http.websocketx.html5.WebSocketServer
。 此处理程序的实现假定您只想运行websocket服务器而不处理其他类型的HTTP请求(如GET和POST)。 如果您希望在一台服务器中同时支持HTTP请求和websockets,请参阅io.netty.example.http.websocketx.server.WebSocketServer示例。 要知道握手完成后,您可以拦截ChannelInboundHandler.userEventTriggered(ChannelHandlerContext,Object)并检查该事件是否是WebSocketServerProtocolHandler.HandshakeComplete的实例,该事件将包含有关握手的额外信息,例如请求和所选子协议。