netty入门(十七)WebSocket长连接开发

命运对每个人都是一样的,不一样的是各自的努力和付出不同,付出的越多,努力的越多,得到的回报也越多,在你累的时候请看一下身边比你成功却还比你更努力的人,这样,你就会更有动力。

导读:本篇文章讲解 netty入门(十七)WebSocket长连接开发,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

1.WebSocket基本介绍

WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。但它跟 HTTP 没什么关系,它是一种基于 TCP 的一种独立实现。

以前客户端想知道服务端的处理进度,要不停地使用 Ajax 进行轮询,让浏览器隔个几秒就向服务器发一次请求,这对服务器压力较高。另外一种轮询就是采用 long poll 的方式,这就跟打电话差不多,没收到消息就一直不挂电话,也就是说,客户端发起连接后,如果没消息,就一直不返回 Response 给客户端,连接阶段一直是阻塞的。

而 WebSocket 解决了 HTTP 的这几个难题。首先,当服务器完成协议升级后( HTTP -> WebSocket ),服务端可以主动推送信息给客户端,解决了轮询造成的同步延迟问题。由于 WebSocket 只需要一次 HTTP 握手,服务端就能一直与客户端保持通讯,直到关闭连接,这样就解决了服务器需要反复解析 HTTP 协议,减少了资源的开销。

netty入门(十七)WebSocket长连接开发

随着新标准的推进,WebSocket 已经比较成熟了,并且各个浏览器对 WebSocket 的支持情况比较好。

使用 WebSocket 的时候,前端使用是比较规范的,js 支持 ws 协议,感觉类似于一个轻度封装的 Socket 协议,只是以前需要自己维护 Socket 的连接,现在能够以比较标准的方法来进行。

2.Netty通过WebSocket编程实现服务器和客户端长链接

实例要求:

(1)Http 协议是无状态的,浏览器和服务器间的请求响应一下,下一次会重新创建连接;

(2)要求:实现基于 WebSocket 的长连接的全双工的交互;

(3)改变 Http 协议多次请求的约束,实现长连接了,服务器可以发送消息给浏览器;

(4)客户端浏览器和服务器端会相互感知,比如服务器关了,浏览器会感知,同样浏览器关了,服务器会感知;

代码实例如下。

服务器端代码:

public class MyServer {
    public static void main(String[] args) throws InterruptedException {
        // 创建两个线程组
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workerGroup = new NioEventLoopGroup(8);

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO)) // 在bossgroup增加一个日志处理器
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();

                            // 因为基于 Http 协议,使用 http 的编码和解码器
                            pipeline.addLast(new HttpServerCodec());
                            // 是以块方式写,添加 ChunkedWrite 处理器
                            pipeline.addLast(new ChunkedWriteHandler());
                            /**
                             * 说明
                             * 1.因为 http 的数据在传输过程中是分段的,HttpObjectAggregator,可以将多个段聚合起来
                             * 2.这就是为什么当浏览器发送大量数据时,就会发出多次 http 请求
                             */
                            pipeline.addLast(new HttpObjectAggregator(8192));
                            /**
                             * 说明
                             * 1.对于websocket,它的数据是以帧的形式传递
                             * 2.可以看到 WebsocketFrame 下面有六个子类
                             * 3.浏览器请求时 ws://localhost:7000/xxx,表示请求的uri
                             * 4.WebSocketServerProtocolHandler 核心功能是将 http 协议升级为 ws 协议,保持长连接
                             * 5.是通过一个 状态码 101
                             */
                            pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));

                            // 自定义的handler,处理业务逻辑
                            pipeline.addLast(new MyTextWebsocketFrameHandler());
                        }
                    });

            ChannelFuture channelFuture = serverBootstrap.bind(7000).sync();
            channelFuture.channel().closeFuture().sync();

        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}

/**
 * 这里的 TextWebSocketFrame 类型,表示一个文本帧(frame)
 */
public class MyTextWebsocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    // 当 web 客户端连接后,触发该方法
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        // id 表示唯一的值,LongText 是唯一的,ShortText不是惟一得
        System.out.println("handlerAdded 被调用" + ctx.channel().id().asLongText());
        System.out.println("handlerAdded 被调用" + ctx.channel().id().asShortText());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerRemoved 被调用" + ctx.channel().id().asLongText());
        System.out.println("handlerRemoved 被调用" + ctx.channel().id().asShortText());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        System.out.println("服务器端收到消息 " + msg.text());

        // 回复消息
        ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间 " + LocalDateTime.now() + " " + msg.text()));

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("异常发生 " + cause.getMessage());
        // 关闭连接
        ctx.close();
    }
}

客户端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script type="text/javascript">
    var socket;
    // 判断当前浏览器是否支持 websocket
    if (window.WebSocket) {
        var socket = new WebSocket("ws://localhost:7000/hello");
        console.log(socket)
        // 相当于 channelRead0,ev 收到服务端端回送的消息
        socket.onmessage = function (ev) {
            var rt = document.getElementById('responseText');
            rt.value = rt.value + "\n" + ev.data;
        }

        // 连接开启
        socket.onopen = function (ev) {
            console.log('websocket open');
            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(msg) {
        // 判断socket是否创建好
        if (!window.socket) {
            console.log(msg)
            return;
        }
        if (socket.readyState == WebSocket.OPEN) {
            // 通过 socket 发送消息
            socket.send(msg);
        }else{
            alert("连接未开启");
        }

    }

</script>
<form onsubmit="return false">
    <textarea name="message" style="height: 300px; width: 300px"></textarea>
    <input type="button" value="发送消息" onclick="send(this.form.message.value)">
    <textarea id="responseText" style="height: 300px; width: 300px"></textarea>
    <input type="button" value="清空内容" onclick="document.getElementById('responseText').value=''">
</form>
</body>
</html>

测试结果:

netty入门(十七)WebSocket长连接开发

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/142583.html

(0)

相关推荐

  • Sharding JDBC 实战 —— 分布式事务处理

    导读:本篇文章讲解 Sharding JDBC 实战 —— 分布式事务处理,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

    后端开发 2022年5月28日
    00
  • SpringBoot单元测试失败,报错Invalid test class ‘com.qfedu.tkmapperdemo.dao.UserDAOTest’

    追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

    导读:本篇文章讲解 SpringBoot单元测试失败,报错Invalid test class ‘com.qfedu.tkmapperdemo.dao.UserDAOTest’,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

    Android 2023年3月27日
    00
  • Vue-07-vue-router路由

    在人生的道路上,不管是潇洒走一回,或者是千山独行,皆须是自己想走的路,虽然,有的人并不是很快就能找到自己的方向和道路,不过,只要坚持到底,我相信,就一定可以找到自己的路,只要找到路,就不必怕路途遥远了。

    导读:本篇文章讲解 Vue-07-vue-router路由,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

    Java自学入门 2023年3月5日
    00
  • Python面试题汇总:高效备战技巧

    命运对每个人都是一样的,不一样的是各自的努力和付出不同,付出的越多,努力的越多,得到的回报也越多,在你累的时候请看一下身边比你成功却还比你更努力的人,这样,你就会更有动力。

    导读:本篇文章讲解 Python面试题汇总:高效备战技巧,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

    后端开发 2023年5月24日
    00
  • 动态规划问题——最长公共子串问题

    导读:本篇文章讲解 动态规划问题——最长公共子串问题,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

    后端开发 2023年2月2日
    00
  • 求解非递减数组范围下标

    导读:本篇文章讲解 求解非递减数组范围下标,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

    Java 2022年5月25日
    00
  • 秒懂Android属性动画

    导读:本篇文章讲解 秒懂Android属性动画,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

    Java 2022年5月24日
    00
  • Jetpack-Compose 学习笔记(二)—— Compose 布局你学会了么?

    最近我们广大程序猿喜提官媒认证:新一代农民工!想不到前几年戏称的“码农”,如今竟一语成谶,哈哈哈!我觉得这是好事,维权啥的更方便了不是么?毕竟农民工工资不能拖欠啊是不是?而且还给我…

    2023年2月13日
    00
  • 使用 FRP 实现内网穿透

    导读:本篇文章讲解 使用 FRP 实现内网穿透,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

    后端开发 2022年5月18日
    00
  • POI导入导出

    导读:本篇文章讲解 POI导入导出,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

    2023年1月3日
    00

发表回复

登录后才能评论