Netty中WebSocket服务端的使用

我们之前介绍的WebSocket集成于SpringBoot中进行实现的,这里我们来看一看WebSocket在Netty中是如何使用的,其实也是比较的简单,首先我们可以依据之前 使用Netty实现Http服务器及客户端,我们把一个简单的页面用于给客户端进行请求,其页面内容如下:
在这里插入图片描述

非常的简单,就是一个div区域用于展示信息,下方一个输入框及按钮,用于发送消息,然后我们就是该页面中还有相关WebSocket的方法,这里我们在 原生WebSocket API实现聊天工具 中也介绍过,这里主要就是在onmessage方法中,接受到消息时,在页面上显示出来,如下:
在这里插入图片描述

然后我们就是我们的发送消息事件了,如下:
在这里插入图片描述

上述就是chat.html 的全部内容了,我们将其放置项目之中,我们就可以在页面进行请求了
在这里插入图片描述


然后服务端就是标准的Netty启动流程,在启动加了些SSL的相关支持,这个我们在实现Http服务器的时候,也有相关的介绍,如下:

public final class WebSocketServer {

    private static boolean SSL = false;
    public static final int PORT = SSL ? 8443 : 8080;

    private void start() throws Exception {
        final SslContext sslContext;
        if (SSL) {
            SelfSignedCertificate signedCertificate = new SelfSignedCertificate();
            sslContext = SslContextBuilder.forServer(signedCertificate.certificate(),
                    signedCertificate.privateKey()).build();
        } else {
            sslContext = null;
        }

        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup work = new NioEventLoopGroup();
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        try {
            serverBootstrap.group(boss, work)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 512)
                    .childHandler(new WebSocketServerInitializer(sslContext));

            ChannelFuture channelFuture = serverBootstrap.bind(PORT).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        WebSocketServer server = new WebSocketServer();
        server.start();
    }
}

然后我们就需要看一看我们添加在相关的Handler了,其中就需要添加相关SSL支持和Http的支持,之前也说过WebSocket是基于Http进行建立的嘛,然后我们还设置了WebSocket的路径,这里的路径和我们在JS中请求的路径是一致的,然后就是添加相关的WebSocket的支持,如下:
在这里插入图片描述
在这里插入图片描述


然后就是我们需要针对Http请求以及WebSocket的请求进行相应的处理,这里针对于Http的请求,主要就是将chat.html 页面返回给浏览器进行展示,这里和 使用Netty实现Http服务器及客户端 中是差不多一致的
在这里插入图片描述

public class WebPageHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        // 状态为1xx的话,继续请求
        if(HttpUtil.is100ContinueExpected(request)){
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
            ctx.writeAndFlush(response);
        }

        //处理错误或者无法解析的http请求
        if (!request.decoderResult().isSuccess()) {
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST,
                    Unpooled.copiedBuffer("请求失败", CharsetUtil.UTF_8));

            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }

        //只允许GET请求
        if (request.method() != HttpMethod.GET) {
            DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN);

            ByteBuf byteBuf = Unpooled.copiedBuffer("只允许GET请求", CharsetUtil.UTF_8);
            response.content().writeBytes(byteBuf);
            byteBuf.release();

            HttpUtil.setContentLength(response, response.content().readableBytes());
            ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }

        if ("/chat".equals(request.uri())) {
            String path = this.getClass().getClassLoader().getResource("static/chat.html").getPath();
            RandomAccessFile file = new RandomAccessFile(path, "r");

            HttpResponse response = new DefaultHttpResponse(request.protocolVersion(), HttpResponseStatus.OK);
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8");

            if (HttpUtil.isKeepAlive(request)) {
                response.headers().set(HttpHeaderNames.CONTENT_LENGTH, file.length());
                response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
            }
            ctx.write(response);

            if (ctx.pipeline().get(SslHandler.class) == null) {
                ctx.write(new DefaultFileRegion(file.getChannel(), 0, file.length()));
            } else {
                ctx.write(new ChunkedNioFile(file.getChannel()));
            }

            ChannelFuture channelFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
            if (!HttpUtil.isKeepAlive(request)) {
                channelFuture.addListener(ChannelFutureListener.CLOSE);
            }
        } else {
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND,
                    Unpooled.copiedBuffer("访问路径错误", CharsetUtil.UTF_8));

            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }
    }
}

最后我们主要需要看一下相关的WebSocket请求的处理,如下:
在这里插入图片描述

public class WebSocketHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame msg) throws Exception {
        if (msg instanceof TextWebSocketFrame) {
            //判断是否为文本帧,若是则进行处理
            String data = ((TextWebSocketFrame) msg).text();
            ctx.channel().writeAndFlush(new TextWebSocketFrame(data));
        }
    }
}

这里我们简单介绍一下 TextWebSocketFrame ,由IETF 发布的WebSocket RFC,定义了6 种帧,Netty 为它们每种都提供了一个POJO 实现,下面我们来看一下 WebSocketFrame 六种不同的类型,前三种为数据帧,后三种为控制帧,其中最后两种帧为心跳报文,ping发起心跳,pong回应心跳。

帧类型 描述
BinaryWebSocketFrame 包含了二进制数据
TextWebSocketFrame 包含了文本数据
ContinuationWebSocketFrame 包含属于上一个BinaryWebSocketFrame或者TextWebSocketFrame的文本数据或者二进制数据
CloseWebSocketFrame 表示一个CLOSE请求,包含一个关闭的状态码和关闭的原因
PingWebSocketFrame 请求传输一个PongWebSocketFrame
PongWebSocketFrame 作为一个对于PingWebSocketFrame的响应被发送

下面就是其运行结果,输入框中输入相关信息,点击发送,就会通过WebSocket发送给服务器,然后服务器又会返回该消息,如下图:
在这里插入图片描述

上述实现的功能虽然和我们之前实现的效果是一直的,但是其实现方式是不同的,我们可以通过前端的JS代码,也能发现,该显示的消息是通过WebSoket进行实现的

扫描二维码关注公众号,回复: 10732799 查看本文章

下面我们进行简单的修改,还可以实现群发的功能,我们还定义了一个事件,用于提醒其他客户端,有新的客户端登录进行来,如下:
在这里插入图片描述
在这里插入图片描述

发布了286 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/newbie0107/article/details/104594237