从零学Netty(八)Netty实现WebSocket服务器

简介

  • WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议
  • WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据
  • 浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输
  • HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯

WebSocket 属性

Socket.readyState

只读属性 readyState 表示连接状态,可以是以下值:

  • 0 - 表示连接尚未建立。

  • 1 - 表示连接已建立,可以进行通信。

  • 2 - 表示连接正在进行关闭。

  • 3 - 表示连接已经关闭或者连接不能打开。

Socket.bufferedAmount

只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。

WebSocket 事件

open Socket.onopen 连接建立时触发
message Socket.onmessage 客户端接收服务端数据时触发
error Socket.onerror 通信发生错误时触发
close Socket.onclose 连接关闭时触发

WebSocket 方法 

Socket.send()

使用连接发送数据

Socket.close()

关闭连接

实例demo

Netty使用WebSocket协议实现服务器

/**
 * ws服务器
 *
 * @author LionLi
 */
public class WebSocketServer {

    public static void main(String[] args) throws Exception {
        // 创建 boss 和 worker 工作组
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup);
            // 主线程处理
            serverBootstrap.channel(NioServerSocketChannel.class);
            // 子线程业务处理
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) {
                    ChannelPipeline pipeline = ch.pipeline();
                    //因为基于http协议,使用http的编码和解码器
                    pipeline.addLast(new HttpServerCodec());
                    //是以块方式写,添加ChunkedWriteHandler处理器
                    pipeline.addLast(new ChunkedWriteHandler());
                    // http数据聚合器 用于将大数据量分段传输的数据 聚合
                    pipeline.addLast(new HttpObjectAggregator(8192));
                    // websocket协议处理器
                    pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
                    // 自定义的业务处理
                    pipeline.addLast(new WebSocketHandler());
                }
            });
            // 启动服务器
            ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

自定义业务处理

/**
 * 业务处理器
 *
 * @author LionLi
 */
// TextWebSocketFrame 文本处理
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    /**
     * 通道消息读取
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
        System.out.println("收到消息 " + msg.text());
        ctx.channel().writeAndFlush(new TextWebSocketFrame("收到消息 " + msg.text()));
    }

    /**
     * 连接初始化
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        System.out.println("客户端连接:通道唯一id为 => " + ctx.channel().id().asLongText());
        System.out.println("客户端连接:通道不唯一id为 => " + ctx.channel().id().asShortText());
    }

    /**
     * 退出连接
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        System.out.println("客户端断开" + ctx.channel().id().asLongText());
    }

    /**
     * 异常处理
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        System.out.println("客户端异常 " + cause.getMessage());
        //关闭连接
        ctx.close();
    }
}

测试

使用页面进行测试

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Netty实现WebSocket服务器</title>
</head>
<body>
<script>
    var socket;
    // 判断当前浏览器是否支持websocket
    if (window.WebSocket) {
        // 创建连接
        socket = new WebSocket("ws://localhost:8088/ws");
        // 消息监听
        socket.onmessage = function (ev) {
            var rt = document.getElementById("responseText");
            rt.value = rt.value + "\n" + ev.data;
        }

        // 连接初始化
        socket.onopen = function (ev) {
            var rt = document.getElementById("responseText");
            rt.value = "连接开启了.."
        }

        // 连接关闭
        socket.onclose = function (ev) {
            var rt = document.getElementById("responseText");
            rt.value = rt.value + "\n" + "连接关闭了.."
        }
    } else {
        alert("当前浏览器不支持websocket")
    }

    // 发送消息
    function send(message) {
        if (!window.socket) {
            return;
        }
        if (socket.readyState === WebSocket.OPEN) {
            socket.send(message)
        } else {
            alert("连接没有开启");
        }
    }
</script>
<form onsubmit="return false">
    <label>
        <textarea name="message" style="height: 300px; width: 300px"></textarea>
    </label>
    <input type="button" value="发送消息" onclick="send(this.form.message.value)">
    <label for="responseText"></label>
    <textarea id="responseText" style="height: 300px; width: 300px"></textarea>
    <input type="button" value="清空内容" onclick="document.getElementById('responseText').value=''">
</form>
</body>
</html>

启动服务器

打开页面 自动连接服务器

发送消息

关闭页面

项目已上传到gitee

地址: netty-demo

如果帮到您了,请帮忙点个star

猜你喜欢

转载自blog.csdn.net/weixin_40461281/article/details/109098194