在上一篇中我们对WebSocket协议进行了介绍,在开始之前,我们还是先看下Netty的整体组件图:
从上图中可以看出Netty支持的协议包括:HTTP&WebSocket,Google Protobuf等协议。
本篇我们就基于Netty来搭建WebSocket服务的完整案例来实现消息的主动推送功能,并在其中贯穿一下上一篇中的WebSocket的部分理论知识!
一、案例实现
2.1 服务端定义
服务端的定义和之前的案例没啥差别(绑定主线程和工作线程),这里为了能够更全面的查看到相关请求的日志信息,就把日志级别调整成了Debug级别,其他的都类似,具体代码实现如下:
package com.rain.interview.netty.netty10; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.protobuf.ProtobufDecoder; import io.netty.handler.codec.protobuf.ProtobufEncoder; import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.stream.ChunkedWriteHandler; /** * <pre>功能描述:</br>Netty基于WebSocket协议栈开发<pre> * @ProjectName prepareInterview * @Author rain * @date 2019-10-16 22:36 * @version v1.0 */ public class WebSocketServer { /** * <pre>服务端绑定逻辑</pre> * * @param port 端口号 * @throws InterruptedException */ public void bind(int port) throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.DEBUG)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline() .addLast("Logging Handler",new LoggingHandler(LogLevel.DEBUG)) .addLast("Http Codec",new HttpServerCodec()) .addLast("Http Aggregator",new HttpObjectAggregator(65535)) .addLast("Http Chunked",new ChunkedWriteHandler()) .addLast("Socket Server",new WebSocketServerHandler()); } }); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { int port = 8080; if (args != null && args.length > 0) { try { port = Integer.valueOf(args[0]); } catch (NumberFormatException e) { } } new WebSocketServer().bind(port); } } |
2.2 消息处理
【代码解读】
由于建立websocket连接的时候,第一次使用的还是http协议,然后判断头部信息中包含Upgrade: websocket
就会进行协议升级,通过WebSocketServerHandshakerFactory创建socket对象并通过handshaker握手建立连接,后续的请求再次进来就可以直接使用第一次创建好的通道,在handlerWebSocketFrame中对websocket消息进行处理,这里针对客户端的几种行为做了判断,比如:关闭请求,Ping请求,消息类型判断(这里暂时支持文本类型的消息)。
具体的代码如下所示:
package com.rain.interview.netty.netty10; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.websocketx.*; import io.netty.util.CharsetUtil; import java.util.Date; import static io.netty.handler.codec.http.HttpUtil.isKeepAlive; import static io.netty.handler.codec.http.HttpUtil.setContentLength; /** * <pre>功能描述:</br>WebSocket处理器<pre> * @ProjectName prepareInterview * @Author rain * @date 2019-10-16 22:39 * @version v1.0 */ public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> { private WebSocketServerHandshaker handshaker; @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("收到消息:"+msg); /** 传统http接入 **/ if (msg instanceof FullHttpRequest) { System.out.println("==========FullHttpRequest Processed ! ============="); handleHttpRequest(ctx, (FullHttpRequest) msg); } /** websocket接入 **/ else if (msg instanceof WebSocketFrame) { System.out.println("==========WebSocketFrame Processed ! ============="); handleWebSocketFrame(ctx, (WebSocketFrame) msg); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } /** * <pre>WebSocket处理</pre> * * @param ctx 上下文 * @param frame websocket */ private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { /** 断开连接处理 **/ if (frame instanceof CloseWebSocketFrame) { handshaker.close(ctx.channel(), ((CloseWebSocketFrame) frame).retain()); return; } /** ping处理 **/ if (frame instanceof PingWebSocketFrame) { ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); return; } /** 非文本不支持 **/ if (!(frame instanceof TextWebSocketFrame)) { throw new UnsupportedOperationException(String.format("%s frame types not supported !", frame.getClass().getName())); } /** 对文本信息处理,并响应客户端 **/ String reqMsg = ((TextWebSocketFrame) frame).text(); System.out.println(String.format("%s received %s ", ctx.channel(), reqMsg)); ctx.channel().write(new TextWebSocketFrame(reqMsg + ",欢迎使用Netty WebSocket 服务,现在时间是:" + new Date().toString())); } /** * <pre>普通HTTP处理</pre> * * @param ctx 上下文 * @param req */ private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) { /** 1.判断解码是否正常并且判断Upgrade是否为websocket **/ if (!req.decoderResult().isSuccess() || (!"websocket".equals(req.headers().get("Upgrade")))) { sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); return; } WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( "ws://localhost:8080/websocket", null, false ); handshaker = wsFactory.newHandshaker(req); if (handshaker == null) { /** 版本不支持 **/ WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); } else { /** 握手建立连接 **/ handshaker.handshake(ctx.channel(), req); } } /** * <pre>发送响应</pre> * * @param ctx 上下文 * @param req 请求 * @param resp 响应 */ private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse resp) { if (resp.status().code() != 200) { ByteBuf buf = Unpooled.copiedBuffer(resp.status().toString(), CharsetUtil.UTF_8); resp.content().writeBytes(buf); buf.release(); setContentLength(resp, resp.content().readableBytes()); } ChannelFuture f = ctx.channel().writeAndFlush(resp); if (!isKeepAlive(req) || resp.status().code() != 200) { f.addListener(ChannelFutureListener.CLOSE); } } } |
2.3 客户端
[1].初始化客户端
const ws = new WebSocket("ws://localhost:8888/websocket"); ws.onmessage=function(event){ let message = { name:'Server', content: event.data } console.info(JSON.stringify(message)); } ws.onopen = function(){ let message = { name:'Server', content: '连接成功!' } console.info(JSON.stringify(message)); } ws.onclose = function(){ let message = { name:'Server', content: '已断开' } console.info(JSON.stringify(message)); } ws.onerror = function(){ let message = { name:'Server', content: '连接错误' } console.info(JSON.stringify(message)); } |
[2].发送消息
ws.send(JSON.stringify({userId:1,content:'I\'m User One'})); |
二、结果分析
[1].服务端响应
[1.1]建立连接
14:48:55.077 [nioEventLoopGroup-2-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9624fea3, L:/0:0:0:0:0:0:0:0:8888] READ: [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] 14:48:55.078 [nioEventLoopGroup-2-1] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0x9624fea3, L:/0:0:0:0:0:0:0:0:8888] READ COMPLETE 14:48:55.079 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] REGISTERED 14:48:55.079 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] ACTIVE 客户端加入连接:[id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] 14:48:55.081 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] READ: 616B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| 47 45 54 20 2f 77 65 62 73 6f 63 6b 65 74 20 48 |GET /websocket H| |00000010| 54 54 50 2f 31 2e 31 0d 0a 48 6f 73 74 3a 20 6c |TTP/1.1..Host: l| |00000020| 6f 63 61 6c 68 6f 73 74 3a 38 38 38 38 0d 0a 43 |ocalhost:8888..C| |00000030| 6f 6e 6e 65 63 74 69 6f 6e 3a 20 55 70 67 72 61 |onnection: Upgra| |00000040| 64 65 0d 0a 50 72 61 67 6d 61 3a 20 6e 6f 2d 63 |de..Pragma: no-c| |00000050| 61 63 68 65 0d 0a 43 61 63 68 65 2d 43 6f 6e 74 |ache..Cache-Cont| |00000060| 72 6f 6c 3a 20 6e 6f 2d 63 61 63 68 65 0d 0a 55 |rol: no-cache..U| |00000070| 73 65 72 2d 41 67 65 6e 74 3a 20 4d 6f 7a 69 6c |ser-Agent: Mozil| |00000080| 6c 61 2f 35 2e 30 20 28 57 69 6e 64 6f 77 73 20 |la/5.0 (Windows | |00000090| 4e 54 20 31 30 2e 30 3b 20 57 69 6e 36 34 3b 20 |NT 10.0; Win64; | |000000a0| 78 36 34 29 20 41 70 70 6c 65 57 65 62 4b 69 74 |x64) AppleWebKit| |000000b0| 2f 35 33 37 2e 33 36 20 28 4b 48 54 4d 4c 2c 20 |/537.36 (KHTML, | |000000c0| 6c 69 6b 65 20 47 65 63 6b 6f 29 20 43 68 72 6f |like Gecko) Chro| |000000d0| 6d 65 2f 37 32 2e 30 2e 33 36 32 36 2e 31 30 39 |me/72.0.3626.109| |000000e0| 20 53 61 66 61 72 69 2f 35 33 37 2e 33 36 0d 0a | Safari/537.36..| |000000f0| 55 70 67 72 61 64 65 3a 20 77 65 62 73 6f 63 6b |Upgrade: websock| |00000100| 65 74 0d 0a 4f 72 69 67 69 6e 3a 20 68 74 74 70 |et..Origin: http| |00000110| 73 3a 2f 2f 31 30 2e 30 2e 31 34 31 2e 31 31 30 |s://10.0.141.110| |00000120| 3a 38 34 34 33 0d 0a 53 65 63 2d 57 65 62 53 6f |:8443..Sec-WebSo| |00000130| 63 6b 65 74 2d 56 65 72 73 69 6f 6e 3a 20 31 33 |cket-Version: 13| |00000140| 0d 0a 41 63 63 65 70 74 2d 45 6e 63 6f 64 69 6e |..Accept-Encodin| |00000150| 67 3a 20 67 7a 69 70 2c 20 64 65 66 6c 61 74 65 |g: gzip, deflate| |00000160| 2c 20 62 72 0d 0a 41 63 63 65 70 74 2d 4c 61 6e |, br..Accept-Lan| |00000170| 67 75 61 67 65 3a 20 7a 68 2d 43 4e 2c 7a 68 3b |guage: zh-CN,zh;| |00000180| 71 3d 30 2e 39 2c 65 6e 3b 71 3d 30 2e 38 0d 0a |q=0.9,en;q=0.8..| |00000190| 43 6f 6f 6b 69 65 3a 20 54 59 5f 53 45 53 53 49 |Cookie: TY_SESSI| |000001a0| 4f 4e 5f 49 44 3d 35 32 62 37 66 62 61 64 2d 66 |ON_ID=52b7fbad-f| |000001b0| 63 31 34 2d 34 37 33 33 2d 38 61 39 33 2d 37 36 |c14-4733-8a93-76| |000001c0| 63 36 34 36 65 62 38 33 61 31 3b 20 6c 63 63 5f |c646eb83a1; lcc_| |000001d0| 73 65 6c 65 63 74 65 64 5f 72 65 67 69 6f 6e 3d |selected_region=| |000001e0| 25 32 32 63 6e 2d 68 61 6e 67 7a 68 6f 75 25 32 |%22cn-hangzhou%2| |000001f0| 32 0d 0a 53 65 63 2d 57 65 62 53 6f 63 6b 65 74 |2..Sec-WebSocket| |00000200| 2d 4b 65 79 3a 20 42 36 37 46 77 36 78 38 71 39 |-Key: B67Fw6x8q9| |00000210| 6c 56 46 6d 73 5a 79 64 73 34 48 77 3d 3d 0d 0a |lVFmsZyds4Hw==..| |00000220| 53 65 63 2d 57 65 62 53 6f 63 6b 65 74 2d 45 78 |Sec-WebSocket-Ex| |00000230| 74 65 6e 73 69 6f 6e 73 3a 20 70 65 72 6d 65 73 |tensions: permes| |00000240| 73 61 67 65 2d 64 65 66 6c 61 74 65 3b 20 63 6c |sage-deflate; cl| |00000250| 69 65 6e 74 5f 6d 61 78 5f 77 69 6e 64 6f 77 5f |ient_max_window_| |00000260| 62 69 74 73 0d 0a 0d 0a |bits.... | +--------+-------------------------------------------------+----------------+ Receive Msg: HttpObjectAggregator$AggregatedFullHttpRequest(decodeResult: success, version: HTTP/1.1, content: CompositeByteBuf(ridx: 0, widx: 0, cap: 0, components=0)) GET /websocket HTTP/1.1 Host: localhost:8888 Connection: Upgrade Pragma: no-cache Cache-Control: no-cache User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36 Upgrade: websocket Origin: https://10.0.141.110:8443 Sec-WebSocket-Version: 13 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Cookie: TY_SESSION_ID=52b7fbad-fc14-4733-8a93-76c646eb83a1; lcc_selected_region=%22cn-hangzhou%22 Sec-WebSocket-Key: B67Fw6x8q9lVFmsZyds4Hw== Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits content-length: 0 FullHttpRequest Process ! 14:48:55.082 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker - [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] WebSocket version V13 server handshake 14:48:55.082 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker - WebSocket version 13 server handshake key: B67Fw6x8q9lVFmsZyds4Hw==, response: X60lyL8CoxqX2HkEL9wVBnHWikc= 14:48:55.083 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] WRITE: 129B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| 48 54 54 50 2f 31 2e 31 20 31 30 31 20 53 77 69 |HTTP/1.1 101 Swi| |00000010| 74 63 68 69 6e 67 20 50 72 6f 74 6f 63 6f 6c 73 |tching Protocols| |00000020| 0d 0a 75 70 67 72 61 64 65 3a 20 77 65 62 73 6f |..upgrade: webso| |00000030| 63 6b 65 74 0d 0a 63 6f 6e 6e 65 63 74 69 6f 6e |cket..connection| |00000040| 3a 20 75 70 67 72 61 64 65 0d 0a 73 65 63 2d 77 |: upgrade..sec-w| |00000050| 65 62 73 6f 63 6b 65 74 2d 61 63 63 65 70 74 3a |ebsocket-accept:| |00000060| 20 58 36 30 6c 79 4c 38 43 6f 78 71 58 32 48 6b | X60lyL8CoxqX2Hk| |00000070| 45 4c 39 77 56 42 6e 48 57 69 6b 63 3d 0d 0a 0d |EL9wVBnHWikc=...| |00000080| 0a |. | +--------+-------------------------------------------------+----------------+ 14:48:55.083 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] FLUSH 14:48:55.083 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] READ COMPLETE 14:48:55.084 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] FLUSH |
[1.2].发送数据
14:49:33.057 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] READ: 57B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| 81 b3 e3 c4 d2 12 98 e6 a7 61 86 b6 9b 76 c1 fe |.........a...v..| |00000010| f0 73 93 ab be 7e 8c e6 fe 30 80 ac b3 66 a0 ab |.s...~...0...f..| |00000020| bc 66 86 aa a6 30 d9 e6 9a 77 8f a8 bd 32 b4 a1 |.f...0...w...2..| |00000030| b0 61 8c a7 b9 77 97 e6 af |.a...w... | +--------+-------------------------------------------------+----------------+ 14:49:33.059 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder - Decoding WebSocket Frame opCode=1 14:49:33.059 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder - Decoding WebSocket Frame length=51 Receive Msg: TextWebSocketFrame(data: PooledUnsafeDirectByteBuf(ridx: 0, widx: 51, cap: 51)) WebSocketFrame Process ! [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] received {"userId":"apollo","chatContent":"Hello Websocket"} 14:49:33.072 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] READ COMPLETE 14:49:33.072 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.codec.http.websocketx.WebSocket08FrameEncoder - Encoding WebSocket Frame opCode=1 length=135 14:49:33.073 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] WRITE: 139B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| 81 7e 00 87 7b 22 75 73 65 72 49 64 22 3a 22 61 |.~..{"userId":"a| |00000010| 70 6f 6c 6c 6f 22 2c 22 63 68 61 74 43 6f 6e 74 |pollo","chatCont| |00000020| 65 6e 74 22 3a 22 48 65 6c 6c 6f 20 57 65 62 73 |ent":"Hello Webs| |00000030| 6f 63 6b 65 74 22 7d 2c e6 ac a2 e8 bf 8e e4 bd |ocket"},........| |00000040| bf e7 94 a8 4e 65 74 74 79 20 57 65 62 53 6f 63 |....Netty WebSoc| |00000050| 6b 65 74 20 e6 9c 8d e5 8a a1 ef bc 8c e7 8e b0 |ket ............| |00000060| e5 9c a8 e6 97 b6 e9 97 b4 e6 98 af ef bc 9a 46 |...............F| |00000070| 72 69 20 4f 63 74 20 31 38 20 31 34 3a 34 39 3a |ri Oct 18 14:49:| |00000080| 33 33 20 43 53 54 20 32 30 31 39 |33 CST 2019 | +--------+-------------------------------------------------+----------------+ 14:49:33.073 [nioEventLoopGroup-3-2] DEBUG io.netty.handler.logging.LoggingHandler - [id: 0xaa3444b2, L:/0:0:0:0:0:0:0:1:8888 - R:/0:0:0:0:0:0:0:1:64829] FLUSH |
[2].客户端响应
[2.1].请求头和响应头
HTTP/1.1 101 Switching Protocols upgrade: websocket connection: upgrade sec-websocket-accept: k0IsewHFp5y6ITP+etLhMQe2hKs= GET ws://localhost:8888/websocket HTTP/1.1 Host: localhost:8888 Connection: Upgrade Pragma: no-cache Cache-Control: no-cache User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36 Upgrade: websocket Origin: https://10.0.141.110 Sec-WebSocket-Version: 13 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Sec-WebSocket-Key: 2NynAl+9iLGheRtvskTC2Q== Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits |
[2.2].客户端响应结果
"{\"userId\":1,\"content\":\"I'm User One\"},欢迎使用Netty WebSocket 服务,现在时间是:Fri Oct 18 14:56:37 CST 2019" |
三、总结
本篇文章,结合Netty搭建了一个简单的WebSocket服务,并且通过浏览器来模拟客户端的连接和请求。上一篇中搭建的WebSocket服务是通过使用SpringBoot+WebSocket实现的,它依赖于tomcat服务运行,虽然看起来也比较简单,但是通过本篇使用Netty来搭建,发现简化了好多,只需要200-300行代码就可以搞定了。看样子,Netty还是非常强大的哈!HTTP协议和WebSocket协议都属于应用层的协议,从下一篇开始,我们将讲讲Netty基于UDP、TCP协议的应用,敬请期待~