• Netty通过心跳保持长链接


       Netty自带心跳检测功能,IdleStateHandler,客户端在写空闲时主动发起心跳请求,服务器接受到心跳请求后给出一个心跳响应。当客户端在一定时间范围内不能够给出响应则断开链接。

    public class NettyClient {
    	public void connect(String remoteServer, int port) throws Exception {
    		EventLoopGroup workerGroup = new NioEventLoopGroup();
    		try {
    			Bootstrap b = new Bootstrap();
    			b.group(workerGroup).channel(NioSocketChannel.class).remoteAddress(remoteServer, port)
    					.handler(new ChildChannelHandler());
    
    			ChannelFuture f = b.connect();
    			System.out.println("Netty time Client connected at port " + port);
    
    			f.channel().closeFuture().sync();
    		} finally {
    			try {
    				TimeUnit.SECONDS.sleep(5);
    				try {
    					System.out.println("重新链接。。。");
    					connect(remoteServer, port);
    				} catch (Exception e) {
    					e.printStackTrace();
    				}
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    	public static class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
    
    		@Override
    		protected void initChannel(final SocketChannel ch) throws Exception {
    			// -8表示lengthAdjustment,让解码器从0开始截取字节,并且包含消息头
    			ch.pipeline().addLast(new RpcEncoder(NettyMessage.class)).addLast(new RpcDecoder(NettyMessage.class))
    					.addLast(new IdleStateHandler(120, 10, 0, TimeUnit.SECONDS)).addLast(new HeartBeatReqHandler());
    		}
    
    	}
    
    	public static void main(String[] args) {
    		try {
    			new NettyClient().connect("127.0.0.1", 12000);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    public class SerializationUtil {
    
        private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();
    
        private static Objenesis                objenesis    = new ObjenesisStd(true);
    
        private static <T> Schema<T> getSchema(Class<T> clazz) {
            @SuppressWarnings("unchecked")
            Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
            if (schema == null) {
                schema = RuntimeSchema.getSchema(clazz);
                if (schema != null) {
                    cachedSchema.put(clazz, schema);
                }
            }
            return schema;
        }
    
        /**
         * 序列化
         *
         * @param obj
         * @return
         */
        public static <T> byte[] serializer(T obj) {
            @SuppressWarnings("unchecked")
            Class<T> clazz = (Class<T>) obj.getClass();
            LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
            try {
                Schema<T> schema = getSchema(clazz);
                byte result[] = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
                return result;
            } catch (Exception e) {
                throw new IllegalStateException(e.getMessage(), e);
            } finally {
                buffer.clear();
            }
        }
    
        /**
         * 反序列化
         *
         * @param data
         * @param clazz
         * @return
         */
        public static <T> T deserializer(byte[] data, Class<T> clazz) {
            try {
                T obj = objenesis.newInstance(clazz);
                Schema<T> schema = getSchema(clazz);
                ProtostuffIOUtil.mergeFrom(data, obj, schema);
                return obj;
            } catch (Exception e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
        }
    }
    
    @SuppressWarnings("rawtypes")
    public class RpcEncoder extends MessageToByteEncoder {
    
        private Class<?> genericClass;
    
        public RpcEncoder(Class<?> genericClass) {
            this.genericClass = genericClass;
        }
    
        @Override
        public void encode(ChannelHandlerContext ctx, Object in, ByteBuf out) throws Exception {
            if (genericClass.isInstance(in)) {
            	System.out.println("发送的请求是:"+in);
                byte[] data = SerializationUtil.serializer(in);
                out.writeInt(data.length);
                out.writeBytes(data);
            }
        }
    }
    
    public class RpcDecoder extends ByteToMessageDecoder {
    
        private Class<?> genericClass;
    
        public RpcDecoder(Class<?> genericClass) {
            this.genericClass = genericClass;
        }
    
        @Override
        public final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
                throws Exception {
            if (in.readableBytes() < 4) {
                return;
            }
            in.markReaderIndex();
            int dataLength = in.readInt();
            if (dataLength < 0) {
                ctx.close();
            }
            if (in.readableBytes() < dataLength) {
                in.resetReaderIndex();
            }
    		byte[] data = new byte[dataLength];
            in.readBytes(data);
    
            Object obj = SerializationUtil.deserializer(data, genericClass);
            System.out.println("接收到的消息是:"+obj);
            out.add(obj);
        }
    }
    public class HeartBeatReqHandler extends ChannelDuplexHandler {
    
    	/**
    	 * @see io.netty.channel.ChannelInboundHandlerAdapter#userEventTriggered(io.netty.channel.ChannelHandlerContext,
    	 *      java.lang.Object)
    	 */
    	@Override
    	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    		if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
    			IdleStateEvent event = (IdleStateEvent) evt;
    			if (event.state() == IdleState.READER_IDLE) {
    				System.out.println("read 空闲");
    				ctx.disconnect();
    			} else if (event.state() == IdleState.WRITER_IDLE) {
    				System.out.println("write 空闲");
    				ctx.writeAndFlush(buildHeartBeat(MessageType.HEARTBEAT_REQ.getType()));
    			}
    		}
    	}
    
    	/**
    	 * 
    	 * @return
    	 * @author zhangwei<wei.zw@corp.netease.com>
    	 */
    	private NettyMessage buildHeartBeat(byte type) {
    		NettyMessage msg = new NettyMessage();
    		Header header = new Header();
    		header.setType(type);
    		msg.setHeader(header);
    		return msg;
    	}
    
    }
    
    public class NettyServer {
    	public void bind(int port) throws Exception {
    		EventLoopGroup bossGroup = new NioEventLoopGroup();
    		EventLoopGroup workerGroup = new NioEventLoopGroup();
    		try {
    			ServerBootstrap b = new ServerBootstrap();
    			b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
    					.childHandler(new ChildChannelHandler());
    
    			ChannelFuture f = b.bind(port).sync();
    			System.out.println("Netty time Server started at port " + port);
    			f.channel().closeFuture().sync();
    		} finally {
    			bossGroup.shutdownGracefully();
    			workerGroup.shutdownGracefully();
    		}
    	}
    
    	public static class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
    
    		@Override
    		protected void initChannel(final SocketChannel ch) throws Exception {
    			ch.pipeline().addLast(new RpcDecoder(NettyMessage.class)).addLast(new RpcEncoder(NettyMessage.class))
    					.addLast(new IdleStateHandler(120, 0, 0, TimeUnit.SECONDS)).addLast(new HeartBeatRespHandler());
    		}
    
    	}
    
    	public static void main(String[] args) {
    		try {
    			new NettyServer().bind(12000);
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    }
    
    public enum MessageType {
    
    	LOGIN_REQ((byte) 1), LOGIN_RESP((byte) 2), HEARTBEAT_REQ((byte) 3), HEARTBEAT_RESP((byte) 4);
    	private byte type;
    
    	/**
    	 * @param type
    	 */
    	private MessageType(byte type) {
    		this.type = type;
    	}
    
    	public byte getType() {
    		return type;
    	}
    
    	public void setType(byte type) {
    		this.type = type;
    	}
    
    	public static MessageType getMessageType(byte type) {
    		for (MessageType b : MessageType.values()) {
    			if (b.getType() == type) {
    				return b;
    			}
    		}
    		return null;
    	}
    
    }
    
    public class HeartBeatRespHandler extends SimpleChannelInboundHandler<NettyMessage> {
    
    	/**
    	 * @see io.netty.channel.SimpleChannelInboundHandler#channelRead0(io.netty.channel.ChannelHandlerContext,
    	 *      java.lang.Object)
    	 */
    	@Override
    	protected void channelRead0(ChannelHandlerContext ctx, NettyMessage msg) throws Exception {
    		if (msg.getHeader() != null && msg.getHeader().getType() == MessageType.HEARTBEAT_REQ.getType()) {
    			NettyMessage heartBeat = buildHeartBeat(MessageType.HEARTBEAT_RESP.getType());
    			ctx.writeAndFlush(heartBeat);
    		} else {
    			ctx.fireChannelRead(msg);
    		}
    	}
    	
    
    	/**
    	 * @see io.netty.channel.ChannelInboundHandlerAdapter#userEventTriggered(io.netty.channel.ChannelHandlerContext,
    	 *      java.lang.Object)
    	 */
    	@Override
    	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    		if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
    			IdleStateEvent event = (IdleStateEvent) evt;
    			if (event.state() == IdleState.READER_IDLE) {
    				System.out.println("read 空闲 关闭链接");
    				ctx.disconnect();	
    			} 
    		}
    	}
    	
    
    	/**
    	 * 
    	 * @return
    	 * @author zhangwei<wei.zw@corp.netease.com>
    	 */
    	private NettyMessage buildHeartBeat(byte type) {
    		NettyMessage msg = new NettyMessage();
    		Header header = new Header();
    		header.setType(type);
    		msg.setHeader(header);
    		return msg;
    	}
    
    }
    
    public class NettyMessage implements Serializable{
    	
    	/**  */
    	private static final long serialVersionUID = 1L;
    
    	private Header header;
    	
    	private Object body;
    
    	public Header getHeader() {
    		return header;
    	}
    
    	public void setHeader(Header header) {
    		this.header = header;
    	}
    
    	public Object getBody() {
    		return body;
    	}
    
    	public void setBody(Object body) {
    		this.body = body;
    	}
    
    	/** 
    	 * @see java.lang.Object#toString()
    	 */
    	@Override
    	public String toString() {
    		return "NettyMessage [header=" + header + ", body=" + body + "]";
    	}
    	
    	
    }
    
    public class Header implements Serializable{
    	/**  */
    	private static final long serialVersionUID = 1L;
    	private int crcCode=0xabef0101;
    	private int length;
    	private long sessionId;
    	private byte type;
    	private byte priority;
    	private Map<String,Object> attachment=new HashMap<>();
    	public int getCrcCode() {
    		return crcCode;
    	}
    	public void setCrcCode(int crcCode) {
    		this.crcCode = crcCode;
    	}
    	public int getLength() {
    		return length;
    	}
    	public void setLength(int length) {
    		this.length = length;
    	}
    	public long getSessionId() {
    		return sessionId;
    	}
    	public void setSessionId(long sessionId) {
    		this.sessionId = sessionId;
    	}
    	public byte getType() {
    		return type;
    	}
    	public void setType(byte type) {
    		this.type = type;
    	}
    	public byte getPriority() {
    		return priority;
    	}
    	public void setPriority(byte priority) {
    		this.priority = priority;
    	}
    	public Map<String, Object> getAttachment() {
    		return attachment;
    	}
    	public void setAttachment(Map<String, Object> attachment) {
    		this.attachment = attachment;
    	}
    	/** 
    	 * @see java.lang.Object#toString()
    	 */
    	@Override
    	public String toString() {
    		return "Header [crcCode=" + crcCode + ", length=" + length + ", sessionId=" + sessionId + ", type=" + type
    				+ ", priority=" + priority + ", attachment=" + attachment + "]";
    	}
    	
    	
    }
    

    客户端的结果是:

    etty time Client connected at port 12000
    write 空闲
    发送的请求是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=3, priority=0, attachment={}], body=null]
    接收到的消息是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=4, priority=0, attachment={}], body=null]
    write 空闲
    发送的请求是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=3, priority=0, attachment={}], body=null]
    接收到的消息是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=4, priority=0, attachment={}], body=null]
    write 空闲
    发送的请求是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=3, priority=0, attachment={}], body=null]
    接收到的消息是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=4, priority=0, attachment={}], body=null]
    write 空闲
    发送的请求是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=3, priority=0, attachment={}], body=null]
    接收到的消息是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=4, priority=0, attachment={}], body=null]
    write 空闲
    发送的请求是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=3, priority=0, attachment={}], body=null]
    接收到的消息是:NettyMessage [header=Header [crcCode=-1410399999, length=0, sessionId=0, type=4, priority=0, attachment={}], body=null]
    
  • 相关阅读:
    P1629 邮递员送信
    P1119 灾后重建
    最短路问题
    P1194 买礼物
    最小生成树
    P1038 神经网络
    P2661 信息传递
    mysql 5.7启动报错
    docker flannel网络部署和路由走向分析
    k8s---无头服务
  • 原文地址:https://www.cnblogs.com/wei-zw/p/8797736.html
Copyright © 2020-2023  润新知