• Netty学习——实战篇5 Netty 心跳监测/WebSocket长连接编程 备份


     1 心跳监测

    MyServer.java
    1. public class MyServer {
    2. public static void main(String[] args) {
    3. NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
    4. NioEventLoopGroup workerGroup = new NioEventLoopGroup();
    5. try {
    6. ServerBootstrap serverBootstrap = new ServerBootstrap();
    7. serverBootstrap.group(bossGroup,workerGroup)
    8. .channel(NioServerSocketChannel.class)
    9. .handler(new LoggingHandler(LogLevel.DEBUG))
    10. .childHandler(new ChannelInitializer() {
    11. @Override
    12. protected void initChannel(SocketChannel ch) throws Exception {
    13. ChannelPipeline pipeline = ch.pipeline();
    14. //加入Netty提供的 IdleStateHandler
    15. /*
    16. 说明:IdleStateHandler 是netty提供的处理空闲状态的处理器
    17. long readerIdleTime:表示多长时间没有读,就会发送一个心跳监测包 检测是否连接
    18. long writerIdelTime:表示多长时间没有写,就会发送一个心跳监测包 监测是否连接
    19. long allIdelTime:表示多长时间没有读写,就会发送一个心跳检测包 监测是否连接
    20. */
    21. pipeline.addLast(new IdleStateHandler(3,5,7, TimeUnit.SECONDS));
    22. //加入自定义Handler,对空闲检测进一步处理
    23. pipeline.addLast(new MyServerHandler());
    24. }
    25. });
    26. ChannelFuture channelFuture = serverBootstrap.bind(8000).sync();
    27. channelFuture.channel().closeFuture().sync();
    28. }catch (Exception e){
    29. e.printStackTrace();
    30. }finally {
    31. bossGroup.shutdownGracefully();
    32. workerGroup.shutdownGracefully();
    33. }
    34. }
    35. }
    MyServerHandler.java 
    1. @Slf4j
    2. public class MyServerHandler extends ChannelInboundHandlerAdapter {
    3. @Override
    4. public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    5. if(evt instanceof IdleStateEvent){
    6. IdleStateEvent event = (IdleStateEvent) evt;
    7. String eventType = null;
    8. switch (event.state()){
    9. case READER_IDLE:
    10. eventType = "读空闲";
    11. break;
    12. case WRITER_IDLE:
    13. eventType = "写空闲";
    14. break;
    15. case ALL_IDLE:
    16. eventType = "读写空闲";
    17. break;
    18. }
    19. log.info("{},---超时时间---,{}",ctx.channel().remoteAddress(),eventType);
    20. log.info("服务器做相应处理");
    21. //如果发生空闲,关闭通道
    22. ctx.channel().close();
    23. }
    24. }
    25. }
    NettyChatClient.java 
    1. @Slf4j
    2. public class NettyChatClient {
    3. private String host;
    4. private int port;
    5. public NettyChatClient(String host, int port) {
    6. this.host = host;
    7. this.port = port;
    8. }
    9. private void run(){
    10. NioEventLoopGroup loopGroup = new NioEventLoopGroup();
    11. try {
    12. Bootstrap bootstrap = new Bootstrap();
    13. bootstrap.group(loopGroup)
    14. .channel(NioSocketChannel.class)
    15. .handler(new ChannelInitializer() {
    16. @Override
    17. protected void initChannel(SocketChannel ch) throws Exception {
    18. ChannelPipeline pipeline = ch.pipeline();
    19. pipeline.addLast("decoder",new StringDecoder());
    20. pipeline.addLast("encoder",new StringEncoder());
    21. pipeline.addLast(new NettyChatClientHandler());
    22. }
    23. });
    24. ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
    25. Channel channel = channelFuture.channel();
    26. log.info("客户端连接成功,地址是:{}",channel.remoteAddress());
    27. Scanner scanner = new Scanner(System.in);
    28. while (scanner.hasNextLine()){
    29. String msg = scanner.nextLine();
    30. channel.writeAndFlush(msg + "\r\n");
    31. }
    32. }catch (Exception e){
    33. e.printStackTrace();
    34. }finally {
    35. loopGroup.shutdownGracefully();
    36. }
    37. }
    38. public static void main(String[] args) {
    39. new NettyChatClient("127.0.0.1",8000).run();
    40. }
    41. }
    NettyChatClientHandler.java
    
    1. public class NettyChatClientHandler extends SimpleChannelInboundHandler {
    2. @Override
    3. protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
    4. System.out.println(msg.trim());
    5. }
    6. }

    服务端运行结果:

    WebSocket实现服务器和客户端长连接

    2.1 需求

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

            (2)实现基于webSocket的长连接的全双工的交互。

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

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

    服务端代码:MyServer.java

    1. public class MyServer {
    2. public static void main(String[] args) {
    3. NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
    4. NioEventLoopGroup workerGroup = new NioEventLoopGroup();
    5. try {
    6. ServerBootstrap serverBootstrap = new ServerBootstrap();
    7. serverBootstrap.group(bossGroup,workerGroup)
    8. .channel(NioServerSocketChannel.class)
    9. .handler(new LoggingHandler(LogLevel.INFO))
    10. .childHandler(new ChannelInitializer() {
    11. @Override
    12. protected void initChannel(SocketChannel ch) throws Exception {
    13. ChannelPipeline pipeline = ch.pipeline();
    14. //基于http协议,使用和图片的编解码
    15. pipeline.addLast(new HttpServerCodec());
    16. //以块方式写,添加chunkedwritehandler处理器
    17. pipeline.addLast(new ChunkedWriteHandler());
    18. /*
    19. 说明
    20. 1. http数据在传输过程中是分段, HttpObjectAggregator ,就是可以将多个段聚合
    21. 2. 这就就是为什么,当浏览器发送大量数据时,就会发出多次http请求
    22. */
    23. pipeline.addLast(new HttpObjectAggregator(8192));
    24. /*
    25. 说明
    26. 1. 对应websocket ,它的数据是以 帧(frame) 形式传递
    27. 2. 可以看到WebSocketFrame 下面有六个子类
    28. 3. 浏览器请求时 ws://localhost:7000/hello 表示请求的uri
    29. 4. WebSocketServerProtocolHandler 核心功能是将 http协议升级为 ws协议 , 保持长连接
    30. 5. 是通过一个 状态码 101
    31. */
    32. pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
    33. //自定义handler
    34. pipeline.addLast(new MyTextWebSocketFrameHandler());
    35. }
    36. });
    37. ChannelFuture channelFuture = serverBootstrap.bind(8000).sync();
    38. channelFuture.channel().closeFuture().sync();
    39. }catch (Exception e){
    40. e.printStackTrace();
    41. }finally {
    42. bossGroup.shutdownGracefully();
    43. workerGroup.shutdownGracefully();
    44. }
    45. }
    46. }

    自定义Handler:MyTextWebSocketFrameHandler.java

    1. @Slf4j
    2. public class MyTextWebSocketFrameHandler extends SimpleChannelInboundHandler {
    3. @Override
    4. protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
    5. log.info("服务器接收消息:{}",msg.text());
    6. //回复消息
    7. ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间"+ LocalDateTime.now()+ " " +msg.text()));
    8. }
    9. @Override
    10. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    11. log.info("发生异常:{}",cause.getMessage());
    12. ctx.close();
    13. }
    14. @Override
    15. public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    16. log.info("handlerAdded 被调用,channel id 是:{}",ctx.channel().id().asLongText());
    17. log.info("handlerAdded 被调用,channel id 是:{}",ctx.channel().id().asShortText());
    18. }
    19. @Override
    20. public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
    21. log.info("handlerRemoved 被调用,channel id 是:{}",ctx.channel().id().asLongText());
    22. }
    23. }

    客户端代码:hello.html

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Titletitle>
    6. head>
    7. <body>
    8. <script>
    9. var socket;
    10. //判断当前浏览器是否支持websocket
    11. if(window.WebSocket) {
    12. //go on
    13. socket = new WebSocket("ws://localhost:8000/hello");
    14. //相当于channelReado, ev 收到服务器端回送的消息
    15. socket.onmessage = function (ev) {
    16. var rt = document.getElementById("responseText");
    17. rt.value = rt.value + "\n" + ev.data;
    18. }
    19. //相当于连接开启(感知到连接开启)
    20. socket.onopen = function (ev) {
    21. var rt = document.getElementById("responseText");
    22. rt.value = "连接开启了.."
    23. }
    24. //相当于连接关闭(感知到连接关闭)
    25. socket.onclose = function (ev) {
    26. var rt = document.getElementById("responseText");
    27. rt.value = rt.value + "\n" + "连接关闭了.."
    28. }
    29. } else {
    30. alert("当前浏览器不支持websocket")
    31. }
    32. //发送消息到服务器
    33. function send(message) {
    34. if(!window.socket) { //先判断socket是否创建好
    35. return;
    36. }
    37. if(socket.readyState == WebSocket.OPEN) {
    38. //通过socket 发送消息
    39. socket.send(message)
    40. } else {
    41. alert("连接没有开启");
    42. }
    43. }
    44. script>
    45. <form onsubmit="return false">
    46. <textarea name="message" style="height: 300px; width: 300px">textarea>
    47. <input type="button" value="发生消息" onclick="send(this.form.message.value)">
    48. <textarea id="responseText" style="height: 300px; width: 300px">textarea>
    49. <input type="button" value="清空内容" onclick="document.getElementById('responseText').value=''">
    50. form>
    51. body>
    52. html>

    服务端运行结果:

    客户端运行结果:

  • 相关阅读:
    [编程基础] Python内置模块collections使用笔记
    Java-基于SSM的校园点餐管理系统
    前端项目中资源请求顺序和dom结构顺序不一致,资源启动器有(索引)解析器和脚本
    comfyui安装指南及animaldiff使用
    进口猫罐头在排行榜中是否靠前?排行榜中靠前的猫罐头测评
    java 代理模式(静态代理、动态代理、JDK动态代理、CGLIB动态代理)详解
    零基础html学习-完结
    全量和已占用字符集
    优秀学习资料汇总
    借助ChatGPT使用Pandas实现Excel数据汇总
  • 原文地址:https://blog.csdn.net/geminigoth/article/details/137724040