[Netty系列]中级篇--011.Netty基于Websocket开发(二)--Netty搭建WebSocket服务器

在上一篇中我们对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协议的应用,敬请期待~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值