前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Netty案例介绍(websocket服务)

Netty案例介绍(websocket服务)

作者头像
用户4919348
发布2020-01-24 09:56:47
2.8K0
发布2020-01-24 09:56:47
举报
文章被收录于专栏:波波烤鸭波波烤鸭

WebSocket案例

1.需求分析

  Http协议是无状态的, 浏览器和服务器间的请求响应一次,下一次会重新创建连接.所有在有些情况下并不是太适用。这时websocket就是我们的一种实现方案,具体的websocket的内容网上很多,自行查阅哦,本文主要是介绍基于netty如何实现websocket通信。

要求

  1. 实现基于webSocket的长连接的全双工的交互
  2. 改变Http协议多次请求的约束,实现长连接了, 服务器可以发送消息给浏览器
  3. 客户端浏览器和服务器端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器会感知

2.案例代码实现

2.1 服务端处理器

  服务器处理器中我们仅仅需要处理请求和客户端连接和端口的情况,具体如下:

代码语言:javascript
复制
package com.dpb.netty.websocket;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

import java.time.LocalDateTime;


/**
 * @program: netty4demo
 * @description: 处理器
 * @author: 波波烤鸭
 * @create: 2019-12-30 22:39
 */
public class SocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        // 打印接收到的消息
        System.out.println("服务端接受到的消息:" + textWebSocketFrame.text());
        // 返回消息给客户端
        channelHandlerContext.writeAndFlush(new TextWebSocketFrame("服务器时间: " + LocalDateTime.now() + "  : " + textWebSocketFrame.text()));
    }

    /**
     * 客户端连接的时候触发
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        // LongText() 唯一的  ShortText() 不唯一
        System.out.println("handlerAdded:" + ctx.channel().id().asLongText());
        System.out.println("handlerAdded:" + ctx.channel().id().asShortText());
    }

    /**
     * 客户端断开连接的时候触发
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerRemoved:" + ctx.channel().id().asLongText());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("异常发生了...");
        ctx.close();
    }
}

2.2 服务端

  服务端代码在原有的基础上需要转换相关的协议,具体如下:

代码语言:javascript
复制
package com.dpb.netty.websocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
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.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

import java.net.ServerSocket;

/**
 * @program: netty4demo
 * @description: 基于WebSocket协议的服务端
 * @author: 波波烤鸭
 * @create: 2019-12-30 22:31
 */
public class SocketServerDemo {

    public static void main(String[] args) throws Exception {
        // 1.创建对应的EventLoopGroup对象
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        try{
            bootstrap.group(bossGroup,workGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // websocket 相关的配置
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            //因为基于http协议,使用http的编码和解码器
                            pipeline.addLast(new HttpServerCodec());
                            //是以块方式写,添加ChunkedWriteHandler处理器
                            pipeline.addLast(new ChunkedWriteHandler());

                            /*
                            说明
                                1. http数据在传输过程中是分段, HttpObjectAggregator ,就是可以将多个段聚合
                                2. 这就就是为什么,当浏览器发送大量数据时,就会发出多次http请求
                             */
                            pipeline.addLast(new HttpObjectAggregator(8192));
                            /*
                            说明
                                1. 对应websocket ,它的数据是以 帧(frame) 形式传递
                                2. 可以看到WebSocketFrame 下面有六个子类
                                3. 浏览器请求时 ws://localhost:8888/hello 表示请求的uri
                                4. WebSocketServerProtocolHandler 核心功能是将 http协议升级为 ws协议 , 保持长连接
                                5. 是通过一个 状态码 101
                             */
                            pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
                            // 自定义handler,处理业务逻辑
                            pipeline.addLast(new SocketServerHandler());

                        }
                    });
            System.out.println("服务启动了....");
            ChannelFuture future = bootstrap.bind(8888).sync();
            future.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

2.3 客户端实现

  在客户端中我们就简单的实现一个HTML页面,在其中发送websocket协议相关的请求,然后接受相关的消息,如下,注意注释

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>websocket案例测试</title>
</head>
<body>
<script>
    var socket;
    //判断当前浏览器是否支持websocket
    if(window.WebSocket) {
        //go on
        socket = new WebSocket("ws://localhost:8888/hello");
        //相当于channelReado, ev 收到服务器端回送的消息
        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) { //先判断socket是否创建好
            return;
        }
        if(socket.readyState == WebSocket.OPEN) {
            //通过socket 发送消息
            socket.send(message)
        } 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>

3.测试分析

  分别运行服务器和客户端,访问测试,如下:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

运行效果:

在这里插入图片描述
在这里插入图片描述

同时在第一次运行的时候打开调试环境:

在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-12-30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • WebSocket案例
    • 1.需求分析
      • 2.案例代码实现
        • 2.1 服务端处理器
        • 2.2 服务端
        • 2.3 客户端实现
      • 3.测试分析
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档