netty的经典demo-具有确认ack和重连的功能

#################以下是客户端的代码

package com.zfsoft.nettyDemo.client;

import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.concurrent.Future;

public class Client {
    
    private final Random random = new Random();

    private final static Logger log = LoggerFactory.getLogger(Client.class);

    private final AtomicInteger requestSequence = new AtomicInteger(0);
    private final Timer timer = new Timer("NettyClient_scanneResponseTable", true);

    private Bootstrap bootstrap = new Bootstrap();
    private EventLoopGroup eventLoopGroupWorker = new NioEventLoopGroup(2, new ThreadFactory() {
        private AtomicInteger threadIndex = new AtomicInteger(-1);

        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, String.format("NettyClient_%d", this.threadIndex.incrementAndGet()));
        }
    });
    private final int connectTimeout = 5000;
    private final String hostname;
    private final int port;
    private final String charsetName = "UTF-8";
    private Channel channel;
    private volatile boolean inited = false;
    private final int sendTimeout = 5;
    private final int waitResponseTimeout = 10;

    protected final ConcurrentMap<Integer, ReponseWrapper> responseTable = new ConcurrentHashMap<Integer, ReponseWrapper>(
            256);

    public Client(String hostname, int port) {
        super();
        this.hostname = hostname;
        this.port = port;
    }
    
    /**
     * 初始化
     */
    private void init() {
        
        log.info("初始化");
        
        final Charset charset = Charset.forName(this.charsetName);
        
        bootstrap.group(eventLoopGroupWorker)//
        .channel(NioSocketChannel.class)//
        .option(ChannelOption.TCP_NODELAY, true)//
        .option(ChannelOption.SO_KEEPALIVE, false)//
        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, this.connectTimeout)//
        .handler(new ChannelInitializer<SocketChannel>() {//
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();
                ByteBuf delimiter = Unpooled.copiedBuffer("\n".getBytes());
                pipeline.addLast(//
                        new StringDecoder(charset), //
                        new DelimiterBasedFrameDecoder(1024, delimiter), //
                        new StringEncoder(charset), //
                        new NettyClinetHandler()//
                        );
            }
        });
    }

    /**
     * 连接服务端
     */
    public void connect() {
        
        if(!inited) {
            this.init();
        }
        
        log.info("开始连接");
        
        final ChannelFuture cf = bootstrap.connect(this.hostname, this.port);
        try {
            cf.await(this.connectTimeout, TimeUnit.SECONDS);
            if (cf.isSuccess()) {
                log.info("连接[{}]成功", cf.channel());
                this.channel = cf.channel();
                this.inited = true;
                this.timer.scheduleAtFixedRate(new TimerTask() {
                    @Override
                    public void run() {
                        try {
                            Client.this.scanResponseTable();
                        } catch (Throwable e) {
                            log.error("scanResponseTable exception", e);
                        }
                    }
                }, 1000 * 3, 1000);
            } else {
                if(!inited) {
                    //是首次连接
                    this.eventLoopGroupWorker.shutdownGracefully();                    
                }else {
                    log.info("继续重连");
                    this.eventLoopGroupWorker.schedule(new ReconnectTask(), nextReconnectDelayTime(), TimeUnit.SECONDS);
                }
            }
        } catch (InterruptedException e) {
            log.error("connect[{}] cause exception", cf.channel(), e);
        }
    }
    
    /**
     * 重连随机时间
     * @return
     */
    protected int nextReconnectDelayTime() {
        return this.random.nextInt(5);
    }
    
    /**
     * 断开连接 
     */
    public void disconnect() {
        this.timer.cancel();
        Future<?> future = this.eventLoopGroupWorker.shutdownGracefully();
        try {
            future.await(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error("断开连接异常",e);
        }
        this.channel.close();
    }

    /**
     * 发送消息,true成功,false失败
     * 
     * @param msg
     * @return
     */
    public boolean send(String msg) {

        final Integer seq = requestSequence.incrementAndGet();

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("seq", seq);
        jsonObject.put("msg", msg);

        final ChannelFuture channelFuture = this.channel.writeAndFlush(jsonObject.toJSONString() + "\n");

        final int timeoutMillis = (this.sendTimeout + this.waitResponseTimeout) * 1000;
        final ReponseWrapper rep = new ReponseWrapper(channelFuture, timeoutMillis);

        channelFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                responseTable.put(seq, rep);
                rep.setSendSuccess(true);
                rep.releaseSendMessage();
            }
        });
        try {
            rep.awaitSendMessage(this.sendTimeout,TimeUnit.SECONDS);
            rep.setSendSuccess(true);
        } catch (InterruptedException e) {
            log.error("awaitSendMessage[{}] cause exception", channelFuture.channel(), e);
            return false;
        }

        if (responseTable.containsKey(seq)) {
            try {
                rep.awaitResponse(this.waitResponseTimeout,TimeUnit.SECONDS);
                return rep.isResponseSuccess();
                
            } catch (InterruptedException e) {
                log.error("awaitResponse[{}] cause exception", channelFuture.channel(), e);
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * 检测请求和响应
     */
    protected void scanResponseTable() {

        Iterator<Entry<Integer, ReponseWrapper>> it = this.responseTable.entrySet().iterator();
        while (it.hasNext()) {
            Entry<Integer, ReponseWrapper> next = it.next();
            ReponseWrapper rep = next.getValue();

            long time = rep.getBeginTimestamp() + rep.getTimeoutMillis() + 1000;
            if (time <= System.currentTimeMillis()) {
                rep.releaseAll();
                it.remove();
                log.warn("remove timeout request, " + rep);
            }
        }
    }

    /**
     * 业务处理
     * @param ctx
     * @param msg
     */
    protected void processMessageReceived(ChannelHandlerContext ctx, String msg) {
        log.trace("接收消息[{}]",msg);
        JSONObject jsonObject = JSONObject.parseObject(msg);
        Integer seq = jsonObject.getInteger("seq");
        final ReponseWrapper responseChannelFutureWrapper = this.responseTable.get(seq);
        if (responseChannelFutureWrapper != null) {
            responseChannelFutureWrapper.setResponseSuccess(true);
            responseChannelFutureWrapper.releaseResponse();
        } else {
            log.warn("不存的请求号[{}]的消息[{}]",seq,msg);
        }
    }

    /**
     * 业务处理
     */
    class NettyClinetHandler extends SimpleChannelInboundHandler<String> {
        
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            processMessageReceived(ctx, msg);
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            log.info("失去连接,开始准备重连[{}]",ctx.channel());
            ctx.executor().schedule(new ReconnectTask(), nextReconnectDelayTime(), TimeUnit.SECONDS);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            log.error("发生异常[{}]",ctx.channel(),cause);
        }
    }

    /**
     * 重连任务
     */
    class ReconnectTask implements Runnable{
        @Override
        public void run() {
            Client.this.connect();
        }
    }

    /**
     * 响应
     */
    class ReponseWrapper {

        private final long beginTimestamp = System.currentTimeMillis();
        private final long timeoutMillis;
        private final ChannelFuture channelFuture;

        private final CountDownLatch sendCountDownLatch = new CountDownLatch(1);
        private final CountDownLatch waitResponseCountDownLatch = new CountDownLatch(1);
        
        private volatile boolean sendSuccess = false;
        private volatile boolean responseSuccess = false;

        public ReponseWrapper(ChannelFuture channelFuture, long timeoutMillis) {
            super();
            this.timeoutMillis = timeoutMillis;
            this.channelFuture = channelFuture;
        }

        public void awaitSendMessage(int sendTimeout,TimeUnit unit) throws InterruptedException {
            this.sendCountDownLatch.await(sendTimeout, unit);
        }

        public void releaseSendMessage() {
            this.sendCountDownLatch.countDown();
        }

        public void awaitResponse(int waitResponseTimeout,TimeUnit unit) throws InterruptedException {
            this.waitResponseCountDownLatch.await(waitResponseTimeout, unit);
        }

        public void releaseResponse() {
            this.waitResponseCountDownLatch.countDown();
        }
        
        public void releaseAll() {
            this.sendCountDownLatch.countDown();
            this.waitResponseCountDownLatch.countDown();
        }

        public long getBeginTimestamp() {
            return beginTimestamp;
        }

        public long getTimeoutMillis() {
            return timeoutMillis;
        }

        public boolean isSendSuccess() {
            return sendSuccess;
        }

        public void setSendSuccess(boolean sendSuccess) {
            this.sendSuccess = sendSuccess;
        }

        public boolean isResponseSuccess() {
            return responseSuccess;
        }

        public void setResponseSuccess(boolean responseSuccess) {
            this.responseSuccess = responseSuccess;
        }

        @Override
        public String toString() {
            return "ReponseWrapper [beginTimestamp=" + beginTimestamp + ", timeoutMillis=" + timeoutMillis
                    + ", channelFuture=" + channelFuture + ", sendCountDownLatch=" + sendCountDownLatch
                    + ", waitResponseCountDownLatch=" + waitResponseCountDownLatch + ", sendSuccess=" + sendSuccess
                    + ", responseSuccess=" + responseSuccess + "]";
        }
    }
}

#######################客户端的测试代码

/**
 * <p>Coyright (R) 2014 正方软件股份有限公司。<p>
 */
package com.zfsoft.nettyDemo;


import com.zfsoft.nettyDemo.client.Client;

public class BootstrapClientDemo {

    public static void main(String[] args) {

        Client client = new Client("127.0.0.1", 9000);
        client.connect();
        try {
            System.out.println("开始发送:"+System.currentTimeMillis());
            client.send("hello,world");
            System.out.println("结束发送:"+System.currentTimeMillis());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

########################以下是服务端的代码

package com.zfsoft.nettyDemo.server;

import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

public class Server {
    
    private AtomicInteger requestSequence = new AtomicInteger(0);

    private final ServerBootstrap serverBootstrap;
    private final EventLoopGroup eventLoopGroupBoss = new NioEventLoopGroup();
    private final EventLoopGroup eventLoopGroupWorker = new NioEventLoopGroup();

    private String charsetName = "UTF-8";

    public Server() {
        this.serverBootstrap = new ServerBootstrap();
    }

    public void startup() {

        final Charset charset = Charset.forName(this.charsetName);

        this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupWorker)
                .channel(NioServerSocketChannel.class)//
                .option(ChannelOption.SO_BACKLOG, 1024)//
                .option(ChannelOption.SO_REUSEADDR, true)//
                .childOption(ChannelOption.TCP_NODELAY, true)//
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        ByteBuf delimiter = Unpooled.copiedBuffer("\n".getBytes());
                        pipeline.addLast(//
                                new StringDecoder(charset), //
                                new DelimiterBasedFrameDecoder(1024,delimiter),//
                                new StringEncoder(charset), //
                                new NettyServerHandler()//
                        );
                    }
                });
        try {
            ChannelFuture sync = this.serverBootstrap.bind(9000).sync();
            sync.get();
            System.out.println("绑定结果:"+sync.isSuccess());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    class NettyServerHandler extends SimpleChannelInboundHandler<String> {
        
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            processMessageReceived(ctx,msg);
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            super.channelActive(ctx);
        }
        
    }

    public void shutdown() {

    }

    protected void processMessageReceived(final ChannelHandlerContext ctx, final String msg) {
        System.out.println("接收消息:"+msg);
        ctx.executor().schedule(new Runnable() {
            @Override
            public void run() {
                //System.out.println("回显消息:"+msg);
                //ctx.channel().writeAndFlush(msg+"\n");
            }
        }, 10, TimeUnit.SECONDS);
    }
}

##########################以下是服务端的测试代码

/**
 * <p>Coyright (R) 2014 正方软件股份有限公司。<p>
 */
package com.zfsoft.nettyDemo;

import java.util.concurrent.TimeUnit;

import com.zfsoft.nettyDemo.server.Server;

public class BootstrapServerDemo {

    public static void main(String[] args) {
        Server s = new Server();
        s.startup();
        try {
            TimeUnit.SECONDS.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

#########################maven依赖

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.zfsoft</groupId>
    <artifactId>NettyDemo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>
 
    <name>NettyDemo</name>
    <url>http://maven.apache.org</url>
 
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.test.skip>false</maven.test.skip>
        <maven.javadoc.skip>true</maven.javadoc.skip>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
 
    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.7</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.7</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.29.Final</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.49</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

 

#################log4j配置文件

log4j.rootCategory=DEBUG,stdout
 
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%p %t %d{yyyy-MM-dd HH:mm:ss}  %C %m%n

package com.zfsoft.nettyDemo.server;
 
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
 
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
 
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
 
public class Server {
    
    private AtomicInteger requestSequence = new AtomicInteger(0);
 
    private final ServerBootstrap serverBootstrap;
    private final EventLoopGroup eventLoopGroupBoss = new NioEventLoopGroup();
    private final EventLoopGroup eventLoopGroupWorker = new NioEventLoopGroup();
 
    private String charsetName = "UTF-8";
 
    public Server() {
        this.serverBootstrap = new ServerBootstrap();
    }
 
    public void startup() {
 
        final Charset charset = Charset.forName(this.charsetName);
 
        this.serverBootstrap.group(this.eventLoopGroupBoss, this.eventLoopGroupWorker)
                .channel(NioServerSocketChannel.class)//
                .option(ChannelOption.SO_BACKLOG, 1024)//
                .option(ChannelOption.SO_REUSEADDR, true)//
                .childOption(ChannelOption.TCP_NODELAY, true)//
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        ByteBuf delimiter = Unpooled.copiedBuffer("\n".getBytes());
                        pipeline.addLast(//
                                new StringDecoder(charset), //
                                new DelimiterBasedFrameDecoder(1024,delimiter),//
                                new StringEncoder(charset), //
                                new NettyServerHandler()//
                        );
                    }
                });
        try {
            ChannelFuture sync = this.serverBootstrap.bind(9000).sync();
            sync.get();
            System.out.println("绑定结果:"+sync.isSuccess());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    class NettyServerHandler extends SimpleChannelInboundHandler<String> {
        
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            processMessageReceived(ctx,msg);
        }
 
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            super.channelActive(ctx);
        }
         
    }
 
    public void shutdown() {
 
    }
 
    protected void processMessageReceived(final ChannelHandlerContext ctx, final String msg) {
        System.out.println("接收消息:"+msg);
        ctx.executor().schedule(new Runnable() {
            @Override
            public void run() {
                //System.out.println("回显消息:"+msg);
                //ctx.channel().writeAndFlush(msg+"\n");
            }
        }, 10, TimeUnit.SECONDS);
    }
}

转载于:https://www.cnblogs.com/weiguangyue/p/9671608.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值