Netty 多客户端连接 重连机制

多客户端启动方式:

Runnable client = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 2; i++) {
                    BootNettyClientThread thread = new BootNettyClientThread(31700,"127.0.0.1");
                    thread.start();
                }
                for (int i = 0; i < 2; i++) {
                    BootNettyClientThread thread = new BootNettyClientThread(31701,"127.0.0.1");
                    thread.start();
                }
                for (int i = 0; i < 2; i++) {
                    BootNettyClientThread thread = new BootNettyClientThread(31702,"127.0.0.1");
                    thread.start();
                }
                
            }
        };
service.execute(client);

BootNettyClientThread代码:

public class BootNettyClientThread extends Thread {
 
    private final int port;
 
    private final String address;
    public BootNettyClientThread(int port, String address){
        this.port = port;
        this.address = address;
    }
 
    public void run() {
        try {
            new BootNettyClient().connect(port, address);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

BootNettyClient代码:

public class BootNettyClient {
    static Integer waitTimes = 1;
    static EventLoopGroup eventLoopGroup = new NioEventLoopGroup();

    /**
     * 初始化Bootstrap
     */
    public static final Bootstrap getBootstrap(EventLoopGroup group) {
        if (null == group) {
            group = eventLoopGroup;
        }
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.SO_KEEPALIVE, true).handler(new BootNettyChannelInitializer<SocketChannel>());
        return bootstrap;
    }

    public void connect(int port, String host) throws Exception {
//        System.err.println("正在进行连接:" + host);
        eventLoopGroup.shutdownGracefully();
        eventLoopGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = getBootstrap(null);

        try {
            bootstrap.remoteAddress(host, port);
            // 异步连接tcp服务端
            ChannelFuture future = bootstrap.connect().addListener((ChannelFuture futureListener) -> {
                final EventLoop eventLoop = futureListener.channel().eventLoop();
                if (futureListener.isSuccess()) {
                    BootNettyClientChannel bootNettyClientChannel = new BootNettyClientChannel();
                    Channel channel = futureListener.channel();
                    String id = futureListener.channel().id().toString();
//                    String id = host;
                    bootNettyClientChannel.setChannel(channel);
                    bootNettyClientChannel.setCode("clientId:" + id);
                    BootNettyClientChannelCache.save("clientId:" + id, bootNettyClientChannel);
                    System.out.println("netty client start success=" + id);
                } else {
//                    System.err.println("连接失败," + waitTimes.toString() + "秒后重新连接:" + host);
                    try {
                        Thread.sleep(waitTimes * 1000);
                    } finally {
                        connect(port, host);
                    }
                }
            });
            future.channel().closeFuture().sync();
        } catch (Exception e) {
//            System.err.println("连接异常," + waitTimes.toString() + "秒后重新连接:" + host);
            try {
                Thread.sleep(waitTimes * 1000);
            } finally {
                connect(port, host);
            }
            e.printStackTrace();
        } finally {
            /**
             * 退出,释放资源
             */
            eventLoopGroup.shutdownGracefully().sync();
        }

    }

}

BootNettyChannelInitializer

@ChannelHandler.Sharable
public class BootNettyChannelInitializer<SocketChannel> extends ChannelInitializer<Channel> {
 
    @Override
    protected void initChannel(Channel ch) throws Exception {
 
        ch.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
        ch.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
        /**
         * 自定义ChannelInboundHandlerAdapter
         */
        ch.pipeline().addLast(new BootNettyChannelInboundHandlerAdapter());
        
    }
 
}

BootNettyChannelInboundHandlerAdapter

@ChannelHandler.Sharable
public class BootNettyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter{
    
    /**
     * 从服务端收到新的数据时,这个方法会在收到消息时被调用
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception, IOException {
        if(msg == null){
            return;
        }
        System.out.println("channelRead:read msg:"+msg.toString());
        BootNettyClientChannel bootNettyClientChannel = BootNettyClientChannelCache.get("clientId:"+ctx.channel().id().toString());
        if(bootNettyClientChannel != null){
            System.out.println("to do");
            bootNettyClientChannel.setLast_data(msg.toString());
        }
 
        //回应服务端
        //ctx.write("I got server message thanks server!");
    }
 
    /**
     * 从服务端收到新的数据、读取完成时调用
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws IOException {
        System.out.println("channelReadComplete");
        ctx.flush();
    }
 
    /**
     * 当出现 Throwable 对象才会被调用,即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws IOException {
//        System.out.println("exceptionCaught");
        cause.printStackTrace();
        ctx.close();//抛出异常,断开与客户端的连接
    }
 
    /**
     * 客户端与服务端第一次建立连接时 执行
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelActive(ctx);
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
//        System.out.println("连接成功:"+clientIp);
    }
 
    /**
     * 客户端与服务端 断连时 执行
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception, IOException {
        super.channelInactive(ctx);
        InetSocketAddress inSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = inSocket.getAddress().getHostAddress();
        Integer port = inSocket.getPort();
        ctx.close(); //断开连接时,必须关闭,否则造成资源浪费
//        System.out.println("channelInactive:"+port);
        System.err.println("异常断开,重新连接:" + clientIp);
        
        BootNettyClientThread thread = new BootNettyClientThread(port,clientIp);
        thread.start();
    }
    
 
    
}

BootNettyClientChannelCache

public class BootNettyClientChannelCache {
 
    public static volatile Map<String, BootNettyClientChannel> channelMapCache = new ConcurrentHashMap<String, BootNettyClientChannel>();
 
    public static void add(String code, BootNettyClientChannel channel){
        channelMapCache.put(code,channel);
    }
 
    public static BootNettyClientChannel get(String code){
        return channelMapCache.get(code);
    }
 
    public static void remove(String code){
        channelMapCache.remove(code);
    }
 
    public static void save(String code, BootNettyClientChannel channel) {
        if(channelMapCache.get(code) == null) {
            add(code,channel);
        }
    }
 
 
}

BootNettyClientChannel(补充get set即可)

public class BootNettyClientChannel {
 
    //    连接客户端唯一的code
    private String code;
    
    //    客户端最新发送的消息内容
    private String last_data;
    
    private transient volatile Channel channel;

测试:

断开连接,服务器先后启动,都长保持总数为6个连接。

心跳(Springboot的定时框架):

@Component
@EnableScheduling
public class ClientHelper {
    
//  使用定时器发送心跳
    @Scheduled(cron = "0/3 * * * * ?")
    public void heart_timer() {
        String back = "heart";
        System.err.println("BootNettyClientChannelCache.channelMapCache.size():"+BootNettyClientChannelCache.channelMapCache.size());
        if(BootNettyClientChannelCache.channelMapCache.size() > 0){
            for (Map.Entry<String, BootNettyClientChannel> entry : BootNettyClientChannelCache.channelMapCache.entrySet()) {
                BootNettyClientChannel bootNettyChannel = entry.getValue();
                System.out.println(bootNettyChannel.getCode());
                try {
                    bootNettyChannel.getChannel().writeAndFlush(Unpooled.buffer().writeBytes(back.getBytes()));
                } catch (Exception e) {
                    continue;
                }
            }
        }
 
    }

也可在连接成功的时候新建线程,单独的线程去发送,线程要保存,在发生异常时要停止。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值