netty实战|Netty进阶 -- WebSocket长连接开发


文章目录

  • ? Netty系列文章
  • 一、WebSocket简介
  • 二、有了HTTP为什么还需要WebSocket?
  • 二、需求说明
  • 三、需求分析
    • ?服务器与浏览器相互感知
    • ?服务器发送消息给客户端
    • ?客户端发送消息给服务器
  • 四、效果图
  • 五、核心源码
    • ??前端源码
    • ??后端源码
  • ??往期精彩热文回顾
  • ?小结

? Netty系列文章 Netty入门 – 什么是Netty?
Netty进阶 – 非阻塞网络编程 实现群聊+私聊+心跳检测系统
一、WebSocket简介 百度百科
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
二、有了HTTP为什么还需要WebSocket? 【netty实战|Netty进阶 -- WebSocket长连接开发】因为HTTP有一个缺陷,就是只能从客户端发起请求,无法从服务器发起请求,所以诞生了WebSocket请求
如下
netty实战|Netty进阶 -- WebSocket长连接开发
文章图片

以上为WebSocket请求URI
二、需求说明 Http协议是无状态的,浏览器和服务器间的请求响应一次,下一次会重新创建连接
  1. 要求:实现基于WebSocket的长连接的全双工的交互
  2. 改变Http协议多次请求的约束,实现长连接,服务器可以发送消息给浏览器
  3. 客户端浏览器和服务器会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器也可感知
三、需求分析 ?服务器与浏览器相互感知 当客户端浏览器上线后,服务器可以感知到并提示用户上线,继承SimpleChannelInboundHandler类并重写handlerAdded
即可感知用户上线
?服务器发送消息给客户端 当连接成功后,服务器重写channelRead0方法并通过全局上下文ctx.channel().writeAndFlush方法发送消息
注意:这里不可以直接写字符串发送,要封装成 TextWebSocketFrame
?客户端发送消息给服务器 客户端发送消息给服务器通过WebSocket的send方法发送即可
四、效果图
五、核心源码 ??前端源码 WebSocket.html
Document> var socket; if (window.WebSocket) { socket = new WebSocket("ws://localhost:7000/hello2"); socket.onmessage = function(ev) { var rt = document.getElementById("responseText"); rt.value = https://www.it610.com/article/rt.value +"\n" + ev.data; document.getElementById("msg").valuehttps://www.it610.com/article/= ''; }//连接开启 socket.onopen = function(ev) { var rt = document.getElementById("responseText"); rt.value = https://www.it610.com/article/rt.value +"已开启连接..."; } //连接关闭 socket.onclose = function(ev) { var rt = document.getElementById("responseText"); rt.value = https://www.it610.com/article/rt.value +"已关闭连接..."; } } else { alert("您的浏览器不支持WebSocket!"); }//发送消息到浏览器 function send(msg) { //判断websocket是否创建好 if (!socket) { return; }if (socket.readyState == WebSocket.OPEN) { socket.send(msg); } else { alert("连接未开启!") } }
客户端
服务器内容
> .msgDiv{ float: left; margin: 20px; }

??后端源码 MyWebSocketServer
package com.wanshi.netty.websocket; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; 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; public class MyWebSocketServer {public static void main(String[] args) {//创建2个线程组 EventLoopGroup boosGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try {//创建服务器监听 ServerBootstrap serverBootstrap = new ServerBootstrap(); //添加前置参数 serverBootstrap.group(boosGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); //因为基于http协议,使用http的编码和解码器 pipeline.addLast(new HttpServerCodec()); //是以块方式写,添加ChunkedWriteHandler处理器 pipeline.addLast(new HttpObjectAggregator(8192)); pipeline.addLast(new WebSocketServerProtocolHandler("/hello2")); pipeline.addLast(new MyWebSocketServerHandler()); } }); ChannelFuture channelFuture = serverBootstrap.bind(7000).sync(); channelFuture.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { boosGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }

核心业务处理器MyWebSocketServerHandler
package com.wanshi.netty.websocket; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.util.Date; public class MyWebSocketServerHandler extends SimpleChannelInboundHandler {private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { System.out.println("服务器接受消息:" + msg.text()); ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间:" + sdf.format(new Date()) + " -- 消息:" + msg.text())); }@Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { System.out.println("handlerAdded被调用:" + ctx.channel().id().asLongText()); System.out.println("handlerAdded被调用:" + ctx.channel().id().asShortText()); System.out.println(sdf.format(new Date()) + "【服务器】用户已连接!"); }@Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { System.out.println("handlerRemoved被调用:" + ctx.channel().id().asLongText()); System.out.println(sdf.format(new Date()) + "【服务器】用户已断开连接"); }@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { System.out.println("发生异常:" + cause.getMessage()); ctx.close(); } }

??往期精彩热文回顾 ?? Netty进阶 – 非阻塞网络编程 实现群聊+私聊+心跳检测系统

?? Postman测试工具调试接口详细教程【向后端发送Json数据并接收返回的Json结果】

?? Java面向对象 — 吃货联盟订餐系统(完整版)

?? 一分钟教你快速 搭建Vue脚手架(Vue-Cli)项目并整合ElementUI

?小结 以上就是【Bug 终结者】对Netty进阶 – WebSocket长连接开发简单的理解,WebSocket建立再TCP之上,可以实现浏览器与服务器的通信,和Http具有很好的兼容性,数据格式比较轻量,性能开销小,通信高效。可以随时与服务器通信,WebSocket有很多的好处,多了解,去慢慢掌握,相信你很快就可以掌握~
如果这篇【文章】有帮助到你,希望可以给【Bug 终结者】点个赞,创作不易,如果有对【后端技术】、【前端领域】感兴趣的小可爱,也欢迎关注?????? 【Bug 终结者】??????,我将会给你带来巨大的【收获与惊喜】!

    推荐阅读