文档章节

Netty就是这么回事(七)

ACRushVoid
 ACRushVoid
发布于 2017/01/16 11:48
字数 735
阅读 109
收藏 1

这一章,主要介绍下Netty的心跳处理,心跳处理在通信开发中是最常用的,服务端通过心跳可以监控客户端的链接状态,进行相应的处理。

记得,之前用NIO做了一个客户端和服务端通信的项目,客户端并不是用java写的,而且一个嵌入式的设备,走的lwapp协议栈,有时候嵌入式设备点击复位或者直接掉电后,服务端还没有反应过来,还认为链接是连接状态,资源也就是一直没有得到释放。早在BIO的时候通过检测返回值是否是-1,异常捕获,setSoTimeout(超时时间)来确定客户端是否连接有效。到了nio只能自己实现一个心跳检测,非常的麻烦。好在Netty为我们提供了IdleStateHandler类来完成心跳检测功能,它非常简单,只有三个参数:public IdleStateHandler(int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdleTimeSeconds) 读超时时间,写超时时间,读写超时时间,然后实现用户事件触发监听userEventTriggered这个方法,在这个方法里做相应的处理就可以了,是不是非常的方便!

下面来看一下服务端的代码:

package com.dlb.note.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;

/**
 * 功能:心跳时间服务器
 * 版本:1.0
 * 日期:2016/12/13 10:51
 * 作者:馟苏
 */
public class IdleTimerServer {
    /**
     * 主函数
     */
    public static void main(String []args) {
        // 配置服务端的NIO线程池
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    // 当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer() {
                        protected void initChannel(Channel channel) throws Exception {
                            // 添加心跳处理器 5s读,5s写,10s读写
                            channel.pipeline().addLast(new IdleStateHandler(5, 5, 10));
                            channel.pipeline().addLast(new IdleTimerServerHandler());
                        }
                    });

            // 绑定端口,同步等待成功
            ChannelFuture future = serverBootstrap.bind(8888).sync();
            System.out.println("服务器在8888端口监听hello");

            // 等待服务端监听端口关闭
            future.channel().closeFuture().sync();
            System.out.println("服务器关闭bye");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 优雅退出,释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

class IdleTimerServerHandler extends ChannelHandlerAdapter {
    // 可读
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 读数据
        ByteBuf buf = (ByteBuf) msg;

        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);

        String body = new String(req, "UTF-8");
        System.out.println("receive:" + body);

        // 写数据
        ByteBuf res = Unpooled.copiedBuffer("hello,client!".getBytes());
        ctx.write(res);
        ctx.flush();
    }

    /**
     * 用户事件触发
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(final ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof IdleStateEvent){ // 接受心跳事件
            IdleStateEvent event = (IdleStateEvent)evt;

            if(event.state() == IdleState.ALL_IDLE){ // 读和写状态
                System.out.println("心跳结束");
                //清除超时会话
                ByteBuf res = Unpooled.copiedBuffer("you will close!".getBytes());
                ChannelFuture writeAndFlush = ctx.writeAndFlush(res);
                // 监听结果
                writeAndFlush.addListener(new ChannelFutureListener() {
                    public void operationComplete(ChannelFuture channelFuture) throws Exception {
                        ctx.channel().close();
                    }
                });
            }
        }
    }

    // 连接
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("client come,ip:" + ctx.channel().remoteAddress());
    }

    // 关闭
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("client close,ip:" + ctx.channel().remoteAddress());
        ctx.close();
    }

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

© 著作权归作者所有

ACRushVoid
粉丝 58
博文 18
码字总数 19761
作品 0
威海
私信 提问
HIbernate BigDecimal映射到MySql float字段出现问题

hibernate中配置 如题,当在插入4571476348901579时,数据库中就变成4571476216774656,试了很多次,只有前七位是一样的,后面在插入就乱了,是什么回事?求解......

RunACoding
2016/10/13
513
5
Dubbo 服务调用 源码学习(下)(七)

笔记简述 本学习笔记接上篇Dubbo 服务调用 源码(上)学习(六),上一篇已经完成了invoker的生成,接下来就是具体的方法调用了,包含了mock测试、负载均衡(不涉及细节)、重试、netty调用、...

jwfy
2018/05/16
0
0
netty 学习 (2)Handler的执行顺序

Handler在netty中,无疑占据着非常重要的地位。Handler与Servlet中的filter很像,通过Handler可以完成通讯报文的解码编码、拦截指定的报文、统一对日志错误进行处理、统一对请求进行计数、控...

yaokangjun
2014/06/04
26.2K
10
Android使用FFmpeg(四)--ffmpeg实现音频播放(使用AudioTrack进行播放)

关于 Android使用FFmpeg(一)--编译ffmpeg Android使用FFmpeg(二)--Android Studio配置ffmpeg Android使用FFmpeg(三)--ffmpeg实现视频播放 Android使用FFmpeg(四)--ffmpeg实现音频播放(使用A...

天王盖地虎626
01/14
24
0
《netty入门与实战》笔记-04:pipeline 与 channelHandler

这一小节,我们将会学习 Netty 里面一大核心组件: Pipeline 与 ChannelHandler Netty 中的 pipeline 和 channelHandler 通过责任链设计模式来组织代码逻辑,并且能够支持逻辑的动态添加和删...

Funcy1122
2018/10/21
102
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring Cloud 笔记之Spring cloud config client

观察者模式它的数据的变化是被动的。 观察者模式在java中的实现: package com.hxq.springcloud.springcloudconfigclient;import org.springframework.context.ApplicationListener;i...

xiaoxiao_go
48分钟前
4
0
CentOS7.6中安装使用fcitx框架

内容目录 一、为什么要使用fcitx?二、安装fcitx框架三、安装搜狗输入法 一、为什么要使用fcitx? Gnome3桌面自带的输入法框架为ibus,而在使用ibus时会时不时出现卡顿无法输入的现象。 搜狗和...

技术训练营
今天
4
0
《Designing.Data-Intensive.Applications》笔记 四

第九章 一致性与共识 分布式系统最重要的的抽象之一是共识(consensus):让所有的节点对某件事达成一致。 最终一致性(eventual consistency)只提供较弱的保证,需要探索更高的一致性保证(stro...

丰田破产标志
今天
7
0
docker 使用mysql

1, 进入容器 比如 myslq1 里面进行操作 docker exec -it mysql1 /bin/bash 2. 退出 容器 交互: exit 3. mysql 启动在容器里面,并且 可以本地连接mysql docker run --name mysql1 --env MY...

之渊
今天
7
0
python数据结构

1、字符串及其方法(案例来自Python-100-Days) def main(): str1 = 'hello, world!' # 通过len函数计算字符串的长度 print(len(str1)) # 13 # 获得字符串首字母大写的...

huijue
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部