记录 Netty demo

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cn_yaojin/article/details/84621942

一、服务端

   

package yaojin;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
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;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import yaojin.coder.CustomerDecoder;
import yaojin.coder.CustomerEncoder;
import yaojin.msgtype.CustomMsg;

import java.net.SocketAddress;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class TestServer {

    public static void main(String[] args) {

        int port = 2020;
        ServerBootstrap bootstrap = new ServerBootstrap();
        EventLoopGroup boss = new NioEventLoopGroup(1, new ThreadFactory() {
            //计数器
            private AtomicInteger threadIndex = new AtomicInteger(0);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,String.format("Boss_%d", this.threadIndex.incrementAndGet()));
            }
        });

        //工作线程池
        EventLoopGroup worker = new NioEventLoopGroup(4, new ThreadFactory() {
            //计数器
            private AtomicInteger threadIndex = new AtomicInteger(0);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,String.format("NettyServerEPOLLSelector_%d_%d", 4, this.threadIndex.incrementAndGet()));
            }
        });

        final DefaultEventExecutorGroup defaultEventExecutorGroup = new DefaultEventExecutorGroup(8, new ThreadFactory() {
            //计数器
            private AtomicInteger threadIndex = new AtomicInteger(0);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "ServerCodecThread_" + this.threadIndex.incrementAndGet());
            }
        });

        bootstrap.group(boss, worker);
        bootstrap.channel(NioServerSocketChannel.class);
        bootstrap.option(ChannelOption.SO_BACKLOG, 1024)
                .option(ChannelOption.SO_REUSEADDR, true)
                .option(ChannelOption.SO_KEEPALIVE, true)
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childOption(ChannelOption.SO_SNDBUF, 1024*1024)// 发送缓冲大小
                .childOption(ChannelOption.SO_RCVBUF, 1024*1024);// 接收缓冲大小
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                //如果消息是以特殊符号结束的,比如 $,那么用以下代码
//                ch.pipeline().addLast(new StringEncoder());
//                // 设置特殊分隔符
//                ByteBuf buf = Unpooled.copiedBuffer("$".getBytes());
//                ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));
//                // 设置字符串形式的解码
//                ch.pipeline().addLast(new StringDecoder());
//                ch.pipeline().addLast(new StringHandler());

                //如果是自定义消息,那么用以下代码
                ch.pipeline().addLast(defaultEventExecutorGroup,
                        new CustomerEncoder(),//编码器
                        new CustomerDecoder(),//解码器
                        new IdleStateHandler(0,0,6, TimeUnit.SECONDS),//心跳检测,不管读写,只要超过6秒钟,就主动释放客户端占用的资源
                        new NettyConnectManageHandler(),//心跳处理
                        new CustomerHandler());//业务处理

            }
        });
        try {
            //绑定端口,开始同步
            ChannelFuture channel = bootstrap.bind(port).sync();
            System.out.println("监听端口:" + 2020);
            //等待服务端监听端口的关闭,
            channel.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

}

/**
 * 心跳超时处理
 */
class NettyConnectManageHandler extends ChannelDuplexHandler {

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof IdleStateEvent){
            IdleState state = ((IdleStateEvent) evt).state();
            if (state.equals(IdleState.ALL_IDLE)) {
                System.out.println("客户端连接通道超时了,通道已经被关闭了");
                //关闭通道
//                ctx.channel().close();
                ctx.channel().close().addListener(new ChannelFutureListener(){
                    @Override
                    public void operationComplete(ChannelFuture channelFuture) throws Exception {
                        System.out.println("通道已经成功被关闭了");
                    }
                });
            }
        }else {
            super.userEventTriggered(ctx,evt);
        }
    }
}

/**
 * 自定义消息处理
 */
class CustomerHandler extends SimpleChannelInboundHandler<Object>{
    @Override
    protected void channelRead0(ChannelHandlerContext ct, Object msg) throws Exception {
        if(msg instanceof CustomMsg){
            CustomMsg customMsg = (CustomMsg) msg;
            //根据type可以区分,如果是心跳包,那么不用理会
            System.out.println("服务器接收到客户端的信息:"+customMsg.getBody()+"___"+customMsg.getFlag());
        }
        SocketAddress remote = ct.channel().remoteAddress();
        String addr = remote != null ? remote.toString() : "";
        System.out.println("服务端地址:"+addr);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        ctx.close();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

/**
 * 特殊分隔符 消息处理器
 */
class StringHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String request = (String) msg;
        System.out.println("Server :" + msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

二、客户端

package yaojin;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import yaojin.coder.CustomerDecoder;
import yaojin.coder.CustomerEncoder;
import yaojin.msgtype.CustomMsg;

import java.util.concurrent.TimeUnit;

public class TestClient {

    private static Channel channel;

    public static void main(String[] args) {
        start();
        send();
    }

    /**
     * 测试发送消息
     */
    public static void send() {
//        try {
//            String msg = "---->hi,my name is cn_yaojin ,that is so happy to meet you ,and you could give me happiness. $_";
//            for (int i = 0; 1 < 2; i++) {
//                System.out.println(i);
//                channel.writeAndFlush(i+msg);
//                Thread.sleep(1000);
//            }
//
//
//
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        CustomMsg customMsg = new CustomMsg();
        customMsg.setType((byte)0xAB);//这里可以区分是业务消息,开始心跳包
        customMsg.setFlag((byte)0xCD);
        customMsg.setLength("Hello,cn_yaojin".length());
        customMsg.setBody("Hello,cn_yaojin");
        channel.writeAndFlush(customMsg);
    }

    public static void start() {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.option(ChannelOption.TCP_NODELAY, true);
        bootstrap.handler(new MyChannelHandler());
        try {
            // 异步链接服务器 同步等待链接成功
            ChannelFuture f = bootstrap.connect("127.0.0.1", 2020).sync();
            channel = f.channel();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class MyChannelHandler extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        //如果消息是以特殊符号 $ 结尾的,用以下处理器
//        ch.pipeline().addLast(new StringEncoder());
//        // 设置特殊分隔符
//        ByteBuf buf = Unpooled.copiedBuffer("$".getBytes());
//        ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));
//        ch.pipeline().addLast(new StringDecoder());
//        // 客户端的处理器
//        ch.pipeline().addLast(new HelloClientHandler());

        //如果消息是自定义类型,那么用以下处理器
        ch.pipeline().addLast(new CustomerEncoder());//编码器
        ch.pipeline().addLast(new CustomerDecoder());//解码器
        ch.pipeline().addLast(new IdleStateHandler(0,0,4, TimeUnit.SECONDS));//心跳设置
        ch.pipeline().addLast(new ClientConnectManageHandler());//心跳处理器
        ch.pipeline().addLast(new ClientHandler());//业务处理器

    }
}
/**
 * 心跳超时处理
 */
class ClientConnectManageHandler extends ChannelDuplexHandler {

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof IdleStateEvent){
            IdleState state = ((IdleStateEvent) evt).state();
            if (state.equals(IdleState.ALL_IDLE)) {
                CustomMsg customMsg = new CustomMsg();
                customMsg.setType((byte)0xAB);//这里需要区分是 心跳包类型
                customMsg.setFlag((byte)0xCD);
                customMsg.setLength("心跳包".length());
                customMsg.setBody("心跳包");
                System.out.println("发送-->心跳包");
                ctx.writeAndFlush(customMsg);
            }
        }else {
            super.userEventTriggered(ctx,evt);
        }
    }
}
/**
 * 自定义消息处理器
 */
class ClientHandler extends ChannelInboundHandlerAdapter{
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("开始连接了");
        //这里也可以发送消息
        
    }
}

/**
 * 分隔符消息处理器
 */
class HelloClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("Server say : " + msg);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Client close ");
        super.channelInactive(ctx);
    }
}

三、编码器

package yaojin.coder;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import yaojin.msgtype.CustomMsg;

/**
 * 消息编码器
 */
public class CustomerEncoder extends MessageToByteEncoder<CustomMsg> {

    @Override
    protected void encode(ChannelHandlerContext ct, CustomMsg msg, ByteBuf out) throws Exception {
        if(null == msg){
            throw new RuntimeException("内容不能为空");
        }
        String body = msg.getBody();
        byte[] data=body.getBytes("utf-8");
        out.writeByte(msg.getType());
        out.writeByte(msg.getFlag());
        out.writeInt(data.length);
        out.writeBytes(data);
    }
}

四、解码器

package yaojin.coder;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import yaojin.msgtype.CustomMsg;

/**
 * 消息解码器
 */
public class CustomerDecoder extends LengthFieldBasedFrameDecoder {

    //判断传送客户端传送过来的数据是否按照协议传输,头部信息的大小应该是 byte+byte+int = 1+1+4 = 6
    private static final int HEADER_SIZE = 6;

    private static int maxFrameLength = 1024 * 4;//数据大小, 4K
    private static int lengthFieldLength = 4;//数据长度
    private static int lengthFieldOffset = 2;//length的起始位置
    private static int lengthAdjustment = 0;
    private static int initialBytesToStrip = 0;//

    public CustomerDecoder() {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);
    }

    /**
     * @param maxFrameLength      解码时,处理每个帧数据的最大长度
     * @param lengthFieldOffset   该帧数据中,存放该帧数据的长度的数据的起始位置
     * @param lengthFieldLength   记录该帧数据长度的字段本身的长度
     * @param lengthAdjustment    修改帧数据长度字段中定义的值,可以为负数
     * @param initialBytesToStrip 解析的时候需要跳过的字节数
     * @param failFast            为true,当frame长度超过maxFrameLength时立即报TooLongFrameException异常,为false,读取完整个帧再报异常
     */
    public CustomerDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip, failFast);
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        System.out.println("服务器开始解码了");
        if(in==null){
            return null;
        }
        if(in.readableBytes()<HEADER_SIZE){
            //说明数据不全
            throw new RuntimeException("数据长度异常");
        }
        byte type = in.readByte();
        byte flag = in.readByte();
        int length = in.readInt();
        //数据长度是否充足
        if(in.readableBytes()<length){
            throw new RuntimeException("数据长度不够");
        }
        //获取数据
        ByteBuf buf = in.readBytes(length);
        //数据内容
        byte[] data = new byte[buf.readableBytes()];
        buf.readBytes(data);
        System.out.println("接收到的数据:"+new String(data,"utf-8"));
        CustomMsg msg = new CustomMsg();
        msg.setType(type);
        msg.setFlag(flag);
        msg.setLength(length);
        msg.setBody(new String(data,"utf-8"));
        return msg;
    }
}

五、自定义消息实体

package yaojin.msgtype;

/**
 * 测试消息实体
 */
public class CustomMsg {

    public static void main(String[] args){
        Integer a = 171;
        String hex = "0X"+Integer.toHexString(a);
        System.out.println(hex);

    }

    //类型 系统编号 0xAB 表示A系统,0xBC 表示B系统
    private byte type;

    //信息标志  0xAB 表示心跳包    0xBC 表示超时包  0xCD 业务信息包
    private byte flag;

    //主题信息的长度
    private int length;

    //主题信息
    private String body;

    public CustomMsg() {

    }

    public CustomMsg(byte type, byte flag, int length, String body) {
        this.type = type;
        this.flag = flag;
        this.length = length;
        this.body = body;
    }

    public byte getType() {
        return type;
    }

    public void setType(byte type) {
        this.type = type;
    }

    public byte getFlag() {
        return flag;
    }

    public void setFlag(byte flag) {
        this.flag = flag;
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }
}

猜你喜欢

转载自blog.csdn.net/cn_yaojin/article/details/84621942
今日推荐