netty 简单使用

	这是一个简单的netty demo,这里不讲原理,这个demo是根据这位大神的文章摸索出来,感谢大神的无私分享 https://www.cnblogs.com/TomSnail/

	首先放一张netty的主从模式的线程模型图,也是出自大神的文章
	![在这里插入图片描述](https://img-blog.csdnimg.cn/20190624171202732.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hpZWxpbnpl,size_16,color_FFFFFF,t_70)

图中又有个NioEventLoop。
Acceptor中的NioEventLoop用于接收TCP连接,初始化参数。
I/O线程池中的NioEventLoop异步读取通信对端的数据,发送读事件到channel
类似与java nio的selector。netty的主从模式就是多个selector 客户端接入线程,然后将接入的链接在分发给第二个NioEventLoop负责从端口读写数据。单线程模式就是一个selector 。

下面上代码,这是一个netty 服务端的 spring boot demo
@Component
public class NettyServer implements ApplicationRunner {

private static Logger logger = LoggerFactory.getLogger(NettyServer.class);

//初始化通道
@Autowired
private NettyServerChannelInitializer nettyServerChannelInitializer;

@Value("${netty.server.port}")
private Integer port;
@Value("${netty.server.ip}")
private String ip;
@Value("${netty.server.bossGroup}")
private Integer bossGroup;
@Value("${netty.server.workerGroup}")
private Integer workerGroup;
private Integer bufferSize = 500;

/**
 * ApplicationRunner 启动类
 * 重写run  可随应用一起启动
 * */
@Override
public void run(ApplicationArguments args){
    start();
}

/**
 * netty start
 * */
public void start() {

    InetSocketAddress address = new InetSocketAddress(ip,port);
   
    //Netty内部都是通过线程在处理各种数据,EventLoopGroup就是用来管理调度他们的,注册Channel,管理他们的生命周期。
    //NioEventLoopGroup是一个处理I/O操作的多线程事件循环

    //bossGroup仅接收客户端连接,不做复杂的逻辑处理,为了尽可能减少资源的占用,取值越小越好
    //就是线程模型中的主Reactor,只负责接入链接,再分发给子Reactor进行读写
    EventLoopGroup bossGroup = new NioEventLoopGroup(this.bossGroup);
    //workerGroup作为worker,处理boss接收的连接的流量和将接收的连接注册进入这个worker,负责读写
    //就是子Reactor
    EventLoopGroup workerGroup = new NioEventLoopGroup(this.workerGroup);

    try {
        //ServerBootstrap负责建立服务端
        ServerBootstrap bootstrap = new ServerBootstrap()
                // 绑定线程池
                .group(bossGroup, workerGroup)
                //指定NioServerSocketChannel类生成channel来接收连接
                //指定为服务端
                .channel(NioServerSocketChannel.class)
				//绑定地址
                .localAddress(address)
                //ChannelInitializer用于配置一个新的Channel
                //用于向你的Channel当中添加ChannelInboundHandler的实现
                .childHandler(nettyServerChannelInitializer)

                //BACKLOG用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度。如果未设置或所设置的值小于1,Java将使用默认值50。
                //Option是为了NioServerSocketChannel设置的,用来接收传入连接的
                .option(ChannelOption.SO_BACKLOG, bufferSize)

                //FixedRecvByteBufAllocator:固定长度的接收缓冲区分配器,由它分配的ByteBuf长度都是固定大小的,并不会根据实际数据报的大小动态收缩。但是,如果容量不足,支持动态扩展。动态扩展是Netty ByteBuf的一项基本功能,与ByteBuf分配器的实现没有关系;
                //AdaptiveRecvByteBufAllocator:容量动态调整的接收缓冲区分配器,它会根据之前Channel接收到的数据报大小进行计算,如果连续填充满接收缓冲区的可写空间,则动态扩展容量。如果连续2次接收到的数据报都小于指定值,则收缩当前的容量,以节约内存。
                .option(ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator.DEFAULT)

                //是否启用心跳保活机制。在双方TCP套接字建立连接后(即都进入ESTABLISHED状态)并且在两个小时左右上层没有任何数据传输的情况下,这套机制才会被激活。
                //childOption是用来给父级ServerChannel之下的Channels设置参数的
                .childOption(ChannelOption.SO_KEEPALIVE, true);

        // 绑定端口,开始接收进来的链接
        ChannelFuture future = bootstrap.bind(address).sync();
        logger.info("=========================netty服务器启动,端口:"+address.getPort()+"=========================");
        //closeFuture()当Channel关闭时返回一个ChannelFuture,用于链路检测
        //sync()会同步等待连接操作结果,用户线程将在此wait(),直到连接操作完成之后,线程被notify(),用户代码继续执行
        future.channel().closeFuture().sync();

    } catch (Exception e) {
        logger.error("netty服务端启动异常:" + e.getMessage());
        throw new RuntimeException("netty服务端启动异常:" + e.getMessage());
    } finally {
        // 释放资源
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}

}

/**
自定义通道初始化
*/
public class NettyServerChannelInitializer extends ChannelInitializer {

@Autowired
private NettyServerHandler nettyServerHandler;

/**
 * 初始化channel  设置调用链
 * socket编程一般的处理流程如下
 * 接收: 接收数据>>解析数据(解码)>>业务处理>>回写数据(编码)
 * 发送: 与接收相反
 * 所以,这个类主要就是设置channel 的处理链路
 * */
@Override
protected void initChannel(SocketChannel channel) throws Exception {

    //DelimiterBasedFrameDecoder 根据包尾解决粘包问题,半包会丢弃
    //这个是处理粘包 或者半包 的问题
    ByteBuf delimiter = Unpooled.copiedBuffer(DefaultConstants.TAIL_DATA);
    channel.pipeline().addLast(new DelimiterBasedFrameDecoder(DefaultConstants.MAX_LENGTH,false,delimiter));
    //编码 
    //channel.pipeline().addLast(new MessageEncoder());
    //解码 
    //channel.pipeline().addLast(new MessageDecoder());
    //心跳设置
    channel.pipeline().addLast("idleState", new IdleStateHandler(DefaultConstants.READER_IDLE, DefaultConstants.WRITER_IDLE, DefaultConstants.ALL_IDLE));

    channel.pipeline().addLast(nettyServerHandler);

}

}

/**

  • 此类处理读写操作

  • */
    public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    /**

    • 有客户端接入时触发这个方法
      */
      @Override
      public void channelActive(ChannelHandlerContext ctx) {

      InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
      String clientIp = insocket.getAddress().getHostAddress();
      log.info(“客户端【” + clientIp + “】发起了一个链接”);
      }

/**
* 有客户端中止链接时候触发
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) {

    try {
        //远程地址信息
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        //客户端id
        String clientIp = insocket.getAddress().getHostAddress();
        log.info("客户端链接中断[IP:" + clientIp + "--->PORT:" + insocket.getPort() + "]");
    }catch (Exception e){
        log.error("客户端链接中断处理异常!!" + e);
    }
}

/**
 * @DESCRIPTION: 有客户端发消息会触发此函数
 * @return: void
 */
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    try {
        //数据包
        byte data[] = (byte[])msg;
        log.info("开始处理消息,消息为:" + Arrays.toString(data));
    }catch (Exception e){
        log.error(Thread.currentThread().getName() + "消息处理异常:" + e);
    }
}

/**
 * @DESCRIPTION: 服务端给客户端发送消息
 * @return: void
 */
public void channelWrite(ChannelId channelId, Object msg) {
    /**
     * 数据回写
     * */
}

/**
 * 检测指定时间内无读写操作时触发,此设置在pipeline链路中设置,其实就是心跳设置
 * */
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {

// //远程地址信息
// InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
// //客户端id
// String clientIp = insocket.getAddress().getHostAddress();
// //通道id
// ChannelId channelId = ctx.channel().id();
// if (evt instanceof IdleStateEvent) {
// IdleStateEvent event = (IdleStateEvent) evt;
// if (event.state() == IdleState.READER_IDLE) {
// log.info(“未在指定时间内接收到客户端【”+clientIp+"】的数据,关闭此链接!");
// ctx.disconnect();
// } else if (event.state() == IdleState.WRITER_IDLE) {
log.info(“Client: " + channelId + " WRITER_IDLE 写超时”);
ctx.disconnect();
// } else if (event.state() == IdleState.ALL_IDLE) {
log.info(“Client: " + channelId + " ALL_IDLE 总超时”);
ctx.disconnect();
// }
// }

}

/**
 * 再通道出现异常时候触发
 * @return: void
 */
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {

    //远程地址信息
    InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
    //客户端id
    String clientIp = insocket.getAddress().getHostAddress();
    ctx.close();
    log.info("通道号【" + ctx.channel().id() + "】" + "发生了错误,此连接被关闭!客户端ip=【" + clientIp + "】");
    log.info("通道号【" + ctx.channel().id() + "】异常信息为:" + cause.getMessage());
}

}

大概就这么多,理解有误的地方,请指正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android可以使用Netty框架来进行网络通信和数据传输。Netty是一个高性能的网络通信框架,它提供了简单易用的API,可以轻松地构建TCP、UDP、HTTP和WebSocket等不同协议的通信应用。 在Android开发中,使用Netty可以实现高效稳定的数据传输和连接管理。通过Netty的异步事件驱动模型,Android应用程序可以更好地处理网络通信并实现高并发的数据传输。而且Netty使用NIO技术,可以大大提高网络通信的效率和吞吐量,并降低资源的占用率。 在使用Netty时,Android应用可以通过定制Handler实现自定义的网络通信协议,并通过管道(Pipeline)将处理逻辑按照顺序链接起来,并交给事件处理器(EventLoop)来处理。这样可以轻松地实现复杂的网络应用。 总之,使用Netty可以为Android应用程序提供一种快速、高效、稳定的网络通信方案,并极大地提高网络通信的效率和数据传输的性能。 ### 回答2: Android可以使用Netty作为网络编程框架,通过Netty可以快速地实现一个高性能的服务器/客户端应用程序。Netty是一个基于Java NIO实现的网络编程框架,它提供了异步的、事件驱动的网络通讯方式,很好地解决了原生的Java NIO编程难度大、学习曲线陡峭的问题。Netty中的核心组件包括了Channel、EventLoop、ChannelHandler等,这些组件共同协作,构建了Netty的基本网络编程框架。 在Android中使用Netty时,我们可以创建一个客户端或服务端应用,连接或监听网络,读写数据等。Netty可以实现多种协议的编解码,例如HTTP、WebSocket、TCP等,非常灵活。此外,Netty还提供了一套高效的线程池机制,可以充分利用CPU资源,提高网络编程的效率。 总的来说,Android使用Netty可以帮助我们更轻松地实现服务器/客户端应用程序,提高网络编程效率和性能。但是,需要注意的是,由于Netty是一个基于Java NIO实现的框架,它的设计思想与传统的I/O编程不同,对于初学者来说难度还是较大,需要花费一定的学习时间。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值