1-3 Netty实现心跳机制

Netty主要使用Bootstrap、ChannelPipeline、Handler 、EventLoop、ByteBuf这几个类

基本概念见:https://blog.csdn.net/crazymakercircle/article/details/84332086#Netty_Bootstrap

channelPipiline设计见:https://blog.csdn.net/weixin_39818173/article/details/86700313

ClientChannelInitializer.java

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;

/**
 * 初始化channel
 * */
public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE,Unpooled.copiedBuffer("$_".getBytes())));
        pipeline.addLast("decode",new StringDecoder());
        pipeline.addLast("encode",new StringEncoder());
        pipeline.addLast(new IdleStateHandler(0,30,0,TimeUnit.SECONDS));
        pipeline.addLast("handler",new NettyClientHandler());
    }
}

NettyClient.java

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class NettyClient {
    static NettyClient nettyClient;
    static final String HOST = "127.0.0.1";
    static final int PORT = 8000;
    public void start(){
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap b = new Bootstrap();
        //保持长连接 CHannelOption.SO_KEEPALIVE
        b.group(group)
                .option(ChannelOption.SO_KEEPALIVE,true)
                .channel(NioSocketChannel.class)
                .handler(new ClientChannelInitializer());
        final ChannelFuture f = b.connect(HOST,PORT);

        //断线重连
        f.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                //channelFuture is false
                if(!future.isSuccess()){
                    final EventLoop loop = future.channel().eventLoop();
                    loop.schedule(new Runnable() {
                        @Override
                        public void run() {
                            log.error("服务器连接不上,开始重连操作...");
                            start();
                        }
                    },1L,TimeUnit.SECONDS);
                }
            }
        });
    }
    public static void main(String...args){
        nettyClient = new NettyClient();
        nettyClient.start();
    }
}

NettyClientHandler.java

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoop;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class NettyClientHandler extends SimpleChannelInboundHandler {
    //1 判断存活 2 Read 3 readcomplete
    //channel读取时调用的方法
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        log.info("Server say :"+msg.toString());
    }

    //channel存活时调用的方法
    @Override
    public void channelActive(ChannelHandlerContext ctx){
        log.info("已连接");
    }

    //channel待用时调用
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("断线了。。。。。。");
        //使用过程中断线重连
        final EventLoop eventLoop = ctx.channel().eventLoop();
        eventLoop.schedule(new Runnable() {
            @Override
            public void run() {
                NettyClient.nettyClient.start();
            }
        }, 1, TimeUnit.SECONDS);

        ctx.fireChannelInactive();
    }

    //用户事件触发
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt)
            throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            //IdleState.READER_IDLE没有收到数据
            if (event.state().equals(IdleState.READER_IDLE)) {
                System.out.println("READER_IDLE");
            //IdleState.READER_IDLE没有数据发送
            } else if (event.state().equals(IdleState.WRITER_IDLE)) {
                /**发送心跳,保持长连接*/
                String  s = "ping$_";
                ctx.channel().writeAndFlush(s);
                log.debug("心跳发送成功!");
                System.out.println("心跳发送成功!");
            //暂时没没有收到或发送任何数据    
            } else if (event.state().equals(IdleState.ALL_IDLE)) {
                System.out.println("ALL_IDLE");
            }
        }
        super.userEventTriggered(ctx, evt);
    }

}

NettyServer.java

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class NettyServer {
    static final int PORT =8000;
    public void bind() throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup,workerGroup)
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.SO_KEEPALIVE,true)
                .option(ChannelOption.SO_BACKLOG,1024)
                .childHandler(new NettyServerChannelInitializer());
        ChannelFuture f = b.bind(PORT).sync();
        //等待服务器监听端口关闭
        f.channel().closeFuture().sync();
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }

    public static void main(String... arg) throws InterruptedException {
        new NettyServer().bind();
    }
}

NettyServer

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class NettyServer {
    static final int PORT =8000;
    public void bind() throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup,workerGroup)
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.SO_KEEPALIVE,true)
                .option(ChannelOption.SO_BACKLOG,1024)
                .childHandler(new NettyServerChannelInitializer());
        ChannelFuture f = b.bind(PORT).sync();
        //等待服务器监听端口关闭
        f.channel().closeFuture().sync();
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }

    public static void main(String... arg) throws InterruptedException {
        new NettyServer().bind();
    }
}

ChannelInitializer.java

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;

public class NettyServerChannelInitializer extends ChannelInitializer<SocketChannel> {

    private NettyServerHandler handler ;

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        //解决TCP粘包拆包的问题,以特定的字符结尾($_)
        pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Unpooled.copiedBuffer("$_".getBytes())));
        //字符串解码和编码
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());
        pipeline.addLast(new IdleStateHandler(40,0,0,TimeUnit.SECONDS));
        //服务器的逻辑
        handler = new NettyServerHandler();
        pipeline.addLast("handler", handler);
    }
}

 

NettyServerHandler.java

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class NettyServerHandler extends SimpleChannelInboundHandler {
    private int counter = 0;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        //成功读取计数为0
        log.info("Client say: "+msg.toString());
        counter=0;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("RemoteAddress : "+ctx.channel().remoteAddress().toString());
        super.channelActive(ctx);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof IdleStateEvent){
            IdleStateEvent event = (IdleStateEvent) evt;
            if(event.state().equals(IdleState.READER_IDLE)){
                //空闲40s后触发丢失
            }
            if(counter >=3){
                //连续丢失3次 断开连接
                ctx.channel().close().sync();
                log.info("已于"+ctx.channel().remoteAddress()+"断开连接");
            }else {
                counter++;
                log.info(ctx.channel().remoteAddress()+"丢失了"+counter+"个包");
            }


        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值