Netty4 Tcp长连接、断开重连、心跳监测、Msgpack编码解码

来源:互联网 发布:沃尔沃销售数据 编辑:程序博客网 时间:2024/06/01 10:49

Netty4 Tcp长连接、断开重连、心跳监测、Msgpack编码解码

结构

这里写图片描述


目录

  1. 导入Msgpack
  2. 创建对象,命名DeviceValue
  3. 创建type标记,命名TypeData
  4. 创建msgpack编码器
  5. 创建msgpack解码器
  6. 创建相同Server端和Client端共有的消息处理类
  7. 创建Client端消息处理类
  8. 创建Server端消息处理类
  9. 创建Client
  10. 创建Server
  11. 运行
  12. 结果

  • 导入Msgpack
compile 'org.msgpack:msgpack:0.6.12'
  • 创建对象,命名DeviceValue(注意:需要在对象头上注入@Message)
package com.zmm.netty4msgpacktest.domain;import org.msgpack.annotation.Message;@Messagepublic class DeviceValue {    private int type;    private int seatId;    private int speed;    private int angle;    public int getType() {        return type;    }    public DeviceValue() {    }    public void setType(int type) {        this.type = type;    }    public int getSeatId() {        return seatId;    }    public void setSeatId(int seatId) {        this.seatId = seatId;    }    public int getSpeed() {        return speed;    }    public void setSpeed(int speed) {        this.speed = speed;    }    public int getAngle() {        return angle;    }    public void setAngle(int angle) {        this.angle = angle;    }    @Override    public String toString() {        return "DeviceValue{" +                "type=" + type +                ", seatId=" + seatId +                ", speed=" + speed +                ", angle=" + angle +                '}';    }}
  • 创建type标记,命名TypeData(目的:通过DeviceValue中的不同type来区分数据类型)
package com.zmm.netty4msgpacktest.domain;public interface TypeData {    //模式    byte PING = 1;    byte PONG = 2;    byte CUSTOME = 3;    //*******************************    byte PING_SEAT = 100;    byte PONG_SEAT = 101;    byte SERVER_RESPONSE = 102;    byte SERVER_RESISTANT = 103;}
  • 创建msgpack编码器(Msgpack的具体用法可以去查一下,网上很多,其传输效率真的很高,很适合大量数据传递的情况)
package com.zmm.netty4msgpacktest.code;import org.msgpack.MessagePack;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.MessageToByteEncoder;public class MsgPackEncode extends MessageToByteEncoder<Object> {    @Override    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {        MessagePack msgPack = new MessagePack();        byte[] raw = msgPack.write(msg);        out.writeBytes(raw);    }}
  • 创建msgpack解码器
package com.zmm.netty4msgpacktest.code;import com.zmm.netty4msgpacktest.domain.DeviceValue;import org.msgpack.MessagePack;import java.util.List;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.MessageToMessageDecoder;public class MsgPackDecode extends MessageToMessageDecoder<ByteBuf> {    @Override    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {        final byte[] array;        final int length=msg.readableBytes();        array=new byte[length];        msg.getBytes(msg.readerIndex(), array,0,length);        MessagePack msgpack=new MessagePack();        out.add(msgpack.read(array, DeviceValue.class));    }}
  • 创建相同Server端和Client端共有的消息处理类(通过传递的type,执行不同的逻辑)
package com.zmm.netty4msgpacktest.common;import com.zmm.netty4msgpacktest.domain.DeviceValue;import com.zmm.netty4msgpacktest.domain.TypeData;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import io.netty.handler.timeout.IdleStateEvent;public abstract class CustomHeartbeatHandler extends ChannelInboundHandlerAdapter {    protected String name;    private int heartbeatCount = 0;    public CustomHeartbeatHandler(String name) {        this.name = name;    }    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        DeviceValue deviceValue = (DeviceValue) msg;        int type = deviceValue.getType();        System.out.println("CustomHeartbeatHandler type="+type);        switch (type){            case 1:                sendPongMsg(ctx);                break;            case 2:                System.out.println(name + " get pong msg from " + ctx.channel().remoteAddress());                break;            case 3:                handleData(ctx, msg);                break;        }    }    protected void sendPingMsg(ChannelHandlerContext context) {        DeviceValue deviceValue = new DeviceValue();        deviceValue.setType(TypeData.PING);        deviceValue.setSpeed(0);        deviceValue.setAngle(15);        deviceValue.setSeatId(TypeData.PING_SEAT);        context.channel().writeAndFlush(deviceValue);        heartbeatCount++;        System.out.println(name + " sent ping msg to " + context.channel().remoteAddress() + ", count: " + heartbeatCount);    }    private void sendPongMsg(ChannelHandlerContext context) {        DeviceValue deviceValue = new DeviceValue();        deviceValue.setType(TypeData.PONG);        deviceValue.setSpeed(0);        deviceValue.setAngle(15);        deviceValue.setSeatId(TypeData.PONG_SEAT);        context.channel().writeAndFlush(deviceValue);        heartbeatCount++;        System.out.println(name + " sent pong msg to " + context.channel().remoteAddress() + ", count: " + heartbeatCount);    }    protected abstract void handleData(ChannelHandlerContext channelHandlerContext, Object msg);    @Override    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {        // IdleStateHandler 所产生的 IdleStateEvent 的处理逻辑.        if (evt instanceof IdleStateEvent) {            IdleStateEvent e = (IdleStateEvent) evt;            switch (e.state()) {                case READER_IDLE:                    handleReaderIdle(ctx);                    break;                case WRITER_IDLE:                    handleWriterIdle(ctx);                    break;                case ALL_IDLE:                    handleAllIdle(ctx);                    break;                default:                    break;            }        }    }    @Override    public void channelActive(ChannelHandlerContext ctx) throws Exception {        System.err.println("---" + ctx.channel().remoteAddress() + " is active---");    }    @Override    public void channelInactive(ChannelHandlerContext ctx) throws Exception {        System.err.println("---" + ctx.channel().remoteAddress() + " is inactive---");    }    protected void handleReaderIdle(ChannelHandlerContext ctx) {        System.err.println("---READER_IDLE---");    }    protected void handleWriterIdle(ChannelHandlerContext ctx) {        System.err.println("---WRITER_IDLE---");    }    protected void handleAllIdle(ChannelHandlerContext ctx) {        System.err.println("---ALL_IDLE---");    }}
  • 创建Client端消息处理类(因为在Client里面每隔2s发送一次数据给服务器,这里接收后就不发送了)
package com.zmm.netty4msgpacktest.client;import com.zmm.netty4msgpacktest.common.CustomHeartbeatHandler;import com.zmm.netty4msgpacktest.domain.DeviceValue;import com.zmm.netty4msgpacktest.domain.TypeData;import io.netty.channel.ChannelHandlerContext;public class ClientHandler extends CustomHeartbeatHandler {    private Client client;    public ClientHandler(Client client) {        super("client");        this.client = client;    }    @Override    protected void handleData(ChannelHandlerContext channelHandlerContext, Object msg) {        DeviceValue deviceValue = (DeviceValue) msg;        System.out.println("client 接收数据:"+deviceValue.toString());//        DeviceValue s = new DeviceValue();//        s.setType(TypeData.CUSTOME);//        s.setSpeed(0);//        s.setAngle(15);//        s.setSeatId(TypeData.SERVER_RESPONSE);//        channelHandlerContext.writeAndFlush(s);    }    @Override    protected void handleAllIdle(ChannelHandlerContext ctx) {        super.handleAllIdle(ctx);        sendPingMsg(ctx);    }    @Override    public void channelInactive(ChannelHandlerContext ctx) throws Exception {        super.channelInactive(ctx);        client.doConnect();    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        System.out.println(name + " exception"+cause.toString());    }}
  • 创建Server端消息处理类(服务端收到数据后,返回固定数据给客户端)
package com.zmm.netty4msgpacktest.server;import com.zmm.netty4msgpacktest.common.CustomHeartbeatHandler;import com.zmm.netty4msgpacktest.domain.DeviceValue;import com.zmm.netty4msgpacktest.domain.TypeData;import io.netty.channel.ChannelHandlerContext;public class ServerHandler extends CustomHeartbeatHandler {    public ServerHandler() {        super("server");    }    @Override    protected void handleData(ChannelHandlerContext channelHandlerContext, Object msg) {        DeviceValue deviceValue = (DeviceValue) msg;        System.out.println("server 接收数据:"+deviceValue.toString());        DeviceValue s = new DeviceValue();        s.setType(TypeData.CUSTOME);        s.setSpeed(0);        s.setAngle(15);        s.setSeatId(TypeData.SERVER_RESPONSE);        channelHandlerContext.writeAndFlush(s);        System.out.println("server 发送数据:"+s.toString());    }    @Override    protected void handleReaderIdle(ChannelHandlerContext ctx) {        super.handleReaderIdle(ctx);        System.err.println("---client " + ctx.channel().remoteAddress().toString() + " reader timeout, close it---");        ctx.close();    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        System.out.println(name+" exception"+cause.toString());    }}
  • 创建Client(这里执行了多个程序,包括连接服务器、重连、定时发送等)
package com.zmm.netty4msgpacktest.client;import com.zmm.netty4msgpacktest.code.MsgPackDecode;import com.zmm.netty4msgpacktest.code.MsgPackEncode;import com.zmm.netty4msgpacktest.domain.DeviceValue;import com.zmm.netty4msgpacktest.domain.TypeData;import java.util.Random;import java.util.concurrent.TimeUnit;import io.netty.bootstrap.Bootstrap;import io.netty.channel.Channel;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelFutureListener;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioSocketChannel;import io.netty.handler.codec.LengthFieldBasedFrameDecoder;import io.netty.handler.timeout.IdleStateHandler;public class Client {    private NioEventLoopGroup workGroup = new NioEventLoopGroup(4);    private Channel channel;    private Bootstrap bootstrap;    public static void main(String[] args) throws Exception {        Client client = new Client();        client.start();        client.sendData();    }    public void start() {        try {            bootstrap = new Bootstrap();            bootstrap                    .group(workGroup)                    .channel(NioSocketChannel.class)                    .handler(new ChannelInitializer<SocketChannel>() {                        protected void initChannel(SocketChannel socketChannel) throws Exception {                            ChannelPipeline p = socketChannel.pipeline();                            p.addLast(new IdleStateHandler(0, 0, 5));//                            p.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4, -4, 0));                            p.addLast(new MsgPackDecode());                            p.addLast(new MsgPackEncode());                            p.addLast(new ClientHandler(Client.this));                        }                    });            doConnect();        } catch (Exception e) {            throw new RuntimeException(e);        }    }    /**     * 重连机制,每隔2s重新连接一次服务器     */    protected void doConnect() {        if (channel != null && channel.isActive()) {            return;        }        ChannelFuture future = bootstrap.connect("127.0.0.1", 12345);        future.addListener(new ChannelFutureListener() {            public void operationComplete(ChannelFuture futureListener) throws Exception {                if (futureListener.isSuccess()) {                    channel = futureListener.channel();                    System.out.println("Connect to server successfully!");                } else {                    System.out.println("Failed to connect to server, try connect after 2s");                    futureListener.channel().eventLoop().schedule(new Runnable() {                        @Override                        public void run() {                            doConnect();                        }                    }, 2, TimeUnit.SECONDS);                }            }        });    }    /**     * 发送数据 每隔2秒发送一次     * @throws Exception     */    public void sendData() throws Exception {        Random random = new Random(System.currentTimeMillis());        for (int i = 0; i < 10000; i++) {            if (channel != null && channel.isActive()) {                DeviceValue deviceValue = new DeviceValue();                deviceValue.setType(TypeData.CUSTOME);                deviceValue.setAngle(i%15);                deviceValue.setSeatId(i%30);                deviceValue.setSpeed(i%120);                System.out.println("client 发送数据:"+deviceValue.toString());                channel.writeAndFlush(deviceValue);            }            Thread.sleep(random.nextInt(20000));        }    }}
  • 创建Server(Server开启一次就够了,不需要重复开启,若是服务器断开,Server和Client不会报异常,Client会继续不断的搜索服务器,直到搜索到为止)
package com.zmm.netty4msgpacktest.server;import com.zmm.netty4msgpacktest.code.MsgPackDecode;import com.zmm.netty4msgpacktest.code.MsgPackEncode;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.Channel;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.codec.LengthFieldBasedFrameDecoder;import io.netty.handler.timeout.IdleStateHandler;public class Server {    public static void main(String[] args) {        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);        NioEventLoopGroup workGroup = new NioEventLoopGroup(4);        try {            ServerBootstrap bootstrap = new ServerBootstrap();            bootstrap                    .group(bossGroup, workGroup)                    .channel(NioServerSocketChannel.class)                    .childHandler(new ChannelInitializer<SocketChannel>() {                        protected void initChannel(SocketChannel socketChannel) throws Exception {                            ChannelPipeline p = socketChannel.pipeline();                            p.addLast(new IdleStateHandler(10, 0, 0));//                            p.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4, -4, 0));                            p.addLast(new MsgPackDecode());                            p.addLast(new MsgPackEncode());                            p.addLast(new ServerHandler());                        }                    });            Channel ch = bootstrap.bind(12345).sync().channel();            System.out.println("------Server Start------");            ch.closeFuture().sync();        } catch (Exception e) {            throw new RuntimeException(e);        } finally {            bossGroup.shutdownGracefully();            workGroup.shutdownGracefully();        }    }}
  • 运行
  • 结果

Server端日志:
这里写图片描述

Client端日志:
这里写图片描述

Github源码:Netty4MsgPackTest

阅读全文
0 0