netty编程

根据netty创建简单的tcp服务器,需要自实现三个类,encoder(用于把对象解析为字节码),decoder(用于把字节码解析为对象), handler(业务逻辑处理)。

这里通过netty简单搭建一个自定义协议的tcp服务器。

encoder

public class MessageEncoder extends
			MessageToByteEncoder<Package>{
		@Override
		public void encode(ChannelHandlerContext ctx, Package dataPackage, ByteBuf out) 
			throws Exception{
			// 业务代码返回的package对象 取其中的数据部分(json格式),转为字节 返回到客户端
			logger.debug("encode message");
			out.writeBytes(dataPackage.data.getBytes("UTF-8"));
		}

decoder 解码时首先读取一个整数,再读取一个换行符,把头部信息读取完毕后,读取包体部分。

public class MessageDecoder extends ByteToMessageDecoder{
		
		private int LINE_MAX_LENGTH = 10;
		@Override
		protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf,
		List<Object>out) throws Exception{
			Package dataPackage = new Package();
			//readLine();
			readHeader(bytebuf, dataPackage);
			byte[] bytes = new byte[dataPackage.length];
			bytebuf.readBytes(bytes);
			String data = new String(bytes, Charset.defaultCharset());
			dataPackage.data = data;
			logger.info("read data " + dataPackage.data);
			out.add(dataPackage);
		}
		
		private void readHeader(ByteBuf bytebuf, Package dataPackage) {
			logger.debug("read header");
			dataPackage.version = readLine(bytebuf);
			dataPackage.method = readLine(bytebuf);
			dataPackage.flag = readLine(bytebuf);
			dataPackage.machineId = readLine(bytebuf);
			dataPackage.length = readLine(bytebuf);
			dataPackage.date = readLine(bytebuf);
			dataPackage.code = readLine(bytebuf);
			logger.debug("length" + dataPackage.length);
		}
		
		private int readLine(ByteBuf buffer) {
			int number = buffer.readInt();
			buffer.readByte();
			return number;
		}		
	}

业务逻辑 此部分搬照博客 https://www.jianshu.com/p/9d89b2299ce4

public class LiveHandler extends SimpleChannelInboundHandler<Package> { // 1

    private static Map<Integer, LiveChannelCache> channelCache = new HashMap<>();
    private Logger logger = LoggerFactory.getLogger(LiveHandler.class);
    
    
    public LiveHandler() {
    	super();
    	logger.debug("logic handler init");
    }
    
   
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Package msg) {
    // context 传过来的数据 会调用此方法解析
    	logger.debug("get a connection");
        Channel channel = ctx.channel();
        final int hashCode = msg.machineId;
        System.out.println("channel hashCode:" + hashCode + " msg:" + msg + " cache:" + channelCache.size());

        if (!channelCache.containsKey(hashCode)) {
            System.out.println("channelCache.containsKey(hashCode), put key:" + hashCode);
            // 关闭连接时 需要把缓存中的项移除
            channel.closeFuture().addListener(future -> {
                logger.info("channel close, remove key:" + hashCode);
                channelCache.remove(hashCode);
            });
            //future 用于判断客户端是否在线,定义一个5s/10s后执行的任务,如果中间有心跳包
            //到达 则取消次任务 重新建立新的 如果没有则到时 判定客户端下线。
            ScheduledFuture scheduledFuture = ctx.executor().schedule(
                    () -> {
                        System.out.println("schedule runs, close channel:" + hashCode);
                        channel.close();
                    }, 10, TimeUnit.SECONDS);
            channelCache.put(hashCode, new LiveChannelCache(channel, scheduledFuture));
        }

        switch (msg.method) {
            case 1: {
                LiveChannelCache cache = channelCache.get(hashCode);
                ScheduledFuture scheduledFuture = ctx.executor().schedule(
                        () -> channel.close(), 5, TimeUnit.SECONDS);
                cache.getScheduledFuture().cancel(true);
                cache.setScheduledFuture(scheduledFuture);
                //ctx.channel().writeAndFlush(msg);
                break;
            }
            case 2: {
            	logger.debug("method2");
            	DataHandler dh = new DataHandler();
            	Package rsp_pkg = dh.handle(msg);
            	//ctx.write(msg);
            	//ctx.flush();
            	//ctx.channel().writeAndFlush(rsp_pkg);
            	logger.debug("after message to context");
            	channelCache.entrySet().stream().forEach(entry -> {
                    Channel otherChannel = entry.getValue().getChannel();
                    otherChannel.writeAndFlush(rsp_pkg);
                });
                break;
            }
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        logger.debug("channelReadComplete");
        super.channelReadComplete(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.debug("exceptionCaught");
        if(null != cause) cause.printStackTrace();
        if(null != ctx) ctx.close();
    }
}

在main中

public class test {
	
	private final int port;
	private static Logger logger = LoggerFactory.getLogger(test.class);
	
	public test(int port) {
		this.port = port;
	}
	
	public static void main(String[] args) throws Exception{
		logger.debug("will start");
		new test(8000).start();
	}
	
	public void start() throws Exception{
		ServerBootstrap b = new ServerBootstrap();
		NioEventLoopGroup group = new NioEventLoopGroup();
		b.group(group).channel(NioServerSocketChannel.class);
		
		b.childHandler(new ChannelInitializer<SocketChannel>(){
			@Override
			public void initChannel(SocketChannel ch) throws Exception{
				logger.debug("init channel" + ch);
				ch.pipeline().addLast("decoder", new MessageDecoder())
				.addLast("encoder", new MessageEncoder())
				.addLast("handler", new LiveHandler());
			}
		});
		
		b.childOption(ChannelOption.SO_KEEPALIVE, Boolean.TRUE);
		b.bind(port).sync();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值