IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Netty学习四-WebSocket协议开发 -> 正文阅读

[网络协议]Netty学习四-WebSocket协议开发

1 HTTP 协议弊端

  • HTTP 协议为半双工协议,半双工协议是指数据可以在客户端和服务端两个方向传输,但是不能同时传输。意味着同一时刻只有一个方向上的数据传送
  • HTTP 消息冗长且繁琐,HTTP 消息包含消息头、消息体、换行符等,通常情况下采用文本方式传输,相对于其他二进制协议效率低下
  • 实时性不高

没有 WebSocket 之前,很多网站进行消息推送都是客户端轮询,服务端返回最新的数据给客户端。这种方式浏览器需要不停地向服务端发送请求,HTTP 的 header 数据比较冗长,有用数据占用比较少,浪费带宽资源

2 WebSocket 协议特点

  • 单一的 TCP 连接,采用全双工通信模式
  • 对代理、防火墙、路由器透明
  • 无头部信息、Cookie和身份验证
  • 无安全开销
  • 通过 ping/pong 保持链路激活
  • 服务器可以主动给客户端推消息而不用客户端轮询

3 WebSocket 协议开发

3.1 WebSocketHandler 编写

@Slf4j
public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {

    private WebSocketServerHandshaker handshaker;

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof IdleStateEvent) {
            log.info("idle connect");
            ctx.writeAndFlush(new TextWebSocketFrame("该连接长时间不活跃,即将关闭"));
            ctx.close();
        } else {
            super.userEventTriggered(ctx, msg);
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpRequest) {
            // 传统http接入。第一次请求由 HTTP 承载,
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            // ws接入
            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();
        cause.printStackTrace();
    }

    private void handleWebSocketFrame(ChannelHandlerContext context, WebSocketFrame frame) {
        // 判断是否是关闭链路指令
        if (frame instanceof CloseWebSocketFrame) {
            log.info("websocket close");
            handshaker.close(context.channel(), (CloseWebSocketFrame) frame.retain());
            return;
        }
        if (frame instanceof PingWebSocketFrame) {
            log.info("websocket receive msg:{}", "ping");
            context.channel().writeAndFlush(new PongWebSocketFrame(frame.content().retain()));
            return;
        }

        if (!(frame instanceof TextWebSocketFrame)) {
            log.warn("websocket not support binary msg");
            throw new UnsupportedOperationException("不支持二进制");
        }

        String message = ((TextWebSocketFrame) frame).text();
        System.out.println("websocket receive msg:" + message);
        context.channel().writeAndFlush(new TextWebSocketFrame("welcome to netty websocket, currentTime:" + DateUtil.datetimeToString(new Date())));
    }

    private void handleHttpRequest(ChannelHandlerContext context, FullHttpRequest request) {
        if (!request.decoderResult().isSuccess() || !StringUtils.equalsIgnoreCase("websocket", request.headers().get("Upgrade"))) {
            sendHttpResponse(context, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
        }

        //可以通过url获取其他参数
        WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory(
                "ws://" + request.headers().get("Host") + "/websocket", null, false
        );
        // 构建握手处理器
        handshaker = factory.newHandshaker(request);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(context.channel());
        }
        //构建握手响应消息返回客户端,同时将 WebSocket 编解码器添加到 ChannelPipeline 中
        handshaker.handshake(context.channel(), request);
    }

    private void sendHttpResponse(ChannelHandlerContext context, FullHttpRequest request, FullHttpResponse response) {
        if (response.status() != HttpResponseStatus.OK) {
            ByteBuf buf = Unpooled.copiedBuffer(response.status().toString(), CharsetUtil.UTF_8);
            response.content().writeBytes(buf);
            buf.release();
            HttpUtil.setContentLength(response, response.content().readableBytes());
        }

        // 如果非Keep-Alive,关闭连接
        ChannelFuture future = context.channel().writeAndFlush(response);
        if (!HttpUtil.isKeepAlive(request) || response.status() != HttpResponseStatus.OK) {
            future.addListener(ChannelFutureListener.CLOSE);
        }
    }
}

3.2 启动类

        NettyUtil.initServer(8080, () -> {
            List<ChannelHandler> handlers = new ArrayList<>();
            // 将请求和应答消息编码或解码为 HTTP 消息
            handlers.add(new HttpServerCodec());
            // 将 HTTP 消息多个部分组合成一个完整的 HTTP 消息
            handlers.add(new HttpObjectAggregator(65536));
            // 支持浏览器和服务端进行 WebSocket 通信
            handlers.add(new ChunkedWriteHandler());
            // 进行设置心跳检测
            handlers.add(new IdleStateHandler(10, 60, 20, TimeUnit.SECONDS));
            handlers.add(new WebSocketHandler());
            return handlers;
        });
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-05-24 18:32:39  更:2022-05-24 18:33:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 12:42:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码