关于netty的一些基础

最近在学习netty这个框架,从一开始的入门到渐渐能够了解和使用netty。其中走了一些弯路,希望能够记录下来,以便后面在学习和使用netty的时候能够进行避免。
本文中所使用netty的版本是4.1.18.Final,在maven的项目中,只需要加入如下的依赖就可以编写netty的代码了

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.18.Final</version>
        </dependency>

在一开始学习netty的时候,建议查看netty的官网,官网上面例子http://netty.io/wiki/user-guide-for-4.x.html 一边看官网的例子一边查看netty的api。如碰到不懂得如何使用的类,可以查看相关的api文档进行了解,api文档里面会有详细的方法说以及使用说明。除此之外,你可以从github上面获得所需要的demo例子,看看其他人是如何进行代码的编写的,从中进行学习和总结。netty的demo例子地址https://github.com/waylau/netty-4-user-guide
下面主要记录一个简单的例子,并且在这个例子上面,自己可以根据自己的需求进行代码的编写。以下都是自己的一些学习心得,方便日后自己查阅。

下面是官网中,DiscardServer服务端的主要代码:

public void run() throws Exception {
     // 用来处理I/O操作的多线程事件循环器
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup();
    try {
        ServerBootstrap b = new ServerBootstrap(); // 启动NIO服务的辅助启动类
        b.group(bossGroup, workerGroup)
         .channel(NioServerSocketChannel.class) // 指定使用NioServerSocketChannel类来举例说明一个新的Channel如何接收进来的连接
         .childHandler(new ChannelInitializer<SocketChannel>() { // 事件处理类
             @Override
             public void initChannel(SocketChannel ch) throws Exception {
                 ch.pipeline().addLast(new DiscardServerHandler());
             }
         })
         .option(ChannelOption.SO_BACKLOG, 128)          // 指定的通道实现的配置参数
         .childOption(ChannelOption.SO_KEEPALIVE, true); // option()是提供给NioServerSocketChannel用来接收进来的连接。childOption()是提供给由父管道ServerChannel接收到的连接,在这个例子中也是NioServerSocketChannel
        // 绑定端口并且接收连接进来的请求
        ChannelFuture f = b.bind(port).sync(); // 绑定端口
        // 等待服务端自己关闭,在这个例子中,服务端的程序是不会关闭的
        f.channel().closeFuture().sync();
    } finally {
        workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
    }
}

在上面的例子中,需要启动两个NioEventLoopGroup,一个作为接收连接进来的请求,一个用来处理用户的请求。具体这两个如何使用,自己也不是很明确,只知道启动netty程序的时候需要用到着两个。
接下来有一个ServerBootstrap这个类是用来启动netty使用的,需要设置上面的两个NioEventLoopGroup。表示接受nio的请求。其中channel(NioServerSocketChannel.class)这个方法表示创建一个NioServerSocketChannel这个实例。childHandler(ChannelHandler childHandler)创建一个ChannelHandler去处理外部channel连接进来的请求。最后option()和childOption()是设置一个参数,具体目前还没有进行了解。
后面就是一些绑定端口,和设置启动的程序了,可以从返回的ChannelFuture 中获得channel,这个channel就是NioServerSocketChannel。
下面将要介绍如何创建ChannelHandler以及如何使用ChannelHandler和netty结合
首先是有连接建立的时候,需要给这些连接设置ChannelHandler。在这个例子中,是使用类ChannelInitializer来处理连接的channel。
关于ChannelInitializer的使用说明:
查看api文档可以看到,ChannelInitializer的继承关系,以及类的定义如下:

该类是继承ChannelInboundHandlerAdapter,故应该是对channel的流入事件起到作用,但是具体的channel的channelHandler的设置是在pipeline的设置里面。这里存在一个疑问,就是不知道这里的ChannelInitializer设置是否可以改成其他的ChannelHandler。
在类ChannelInitializer中,有个initChannel的方法,这个方法可以对注册进来的channel进行初始化,即设置channel的pipeline等信息。
在类ChannelInitializer中,有添加注释@ChannelHandler.Sharable。表示该channelHandler可以被重复使用,但要注意被多个channel使用的时候,因为只有一个实例,所以要注意变量在多个实例中共享的问题。如果没有设置@ChannelHandler.Sharable则表示只有一个channel能够进行连接。并且如果这个连接的channel关闭了,就再也不能进行连接了。这个还不知道什么原因
下面介绍下channel里面的ChannelPipeline
获取ChannelPipeline的方式是通过channel进行获得的,在创建channel的时候就会自动创建了pipeline。
ChannelPipeline里面可以增加一系列进入事件和流出操作的Channel。
一个I/O操作被一个ChannelInboundHandler或者一个ChannelOutboundHandler所控制着,通过ChannelHandlerContext可以将事件传递给最靠近的channelHandler去处理。channelHandler的处理顺序如下:
ChannelPipeline p = …;
p.addLast(“1”, new InboundHandlerA());
p.addLast(“2”, new InboundHandlerB());
p.addLast(“3”, new OutboundHandlerA());
p.addLast(“4”, new OutboundHandlerB());
p.addLast(“5”, new InboundOutboundHandlerX());
流入事件的处理顺序为:125
流出事件的处理顺序为:543
并且如果被最近的一个Handler处理了,则不会被传递给下一个Handler。

能被传递的事件有下面的这些事件:

● Inbound event propagation methods:
○ ChannelHandlerContext.fireChannelRegistered()
○ ChannelHandlerContext.fireChannelActive()
○ ChannelHandlerContext.fireChannelRead(Object)
○ ChannelHandlerContext.fireChannelReadComplete()
○ ChannelHandlerContext.fireExceptionCaught(Throwable)
○ ChannelHandlerContext.fireUserEventTriggered(Object)
○ ChannelHandlerContext.fireChannelWritabilityChanged()
○ ChannelHandlerContext.fireChannelInactive()
○ ChannelHandlerContext.fireChannelUnregistered()
● Outbound event propagation methods:
○ ChannelOutboundInvoker.bind(SocketAddress, ChannelPromise)
○ ChannelOutboundInvoker.connect(SocketAddress, SocketAddress, ChannelPromise)
○ ChannelOutboundInvoker.write(Object, ChannelPromise)
○ ChannelHandlerContext.flush()
○ ChannelHandlerContext.read()
○ ChannelOutboundInvoker.disconnect(ChannelPromise)
○ ChannelOutboundInvoker.close(ChannelPromise)
○ ChannelOutboundInvoker.deregister(ChannelPromise)


关于一个事件被一个ChannelHandler获取后,如何传递给下一个事件,这个是否能够实现。以及流出操作如何进行拦截的例子,这个需要验证。
接着介绍一些ChannelHandler的使用:
ChannelHandler用于控制I/O事件或者拦截I/O操作,并且可以将事件通过ChannelPipeline传递给下一个ChannelHandler。
可以通过成员变量来设置ChannelHandler的状态,这样对于每一个新的channel,都需要创建一个ChannelHandler去处理。即代码如下:


public class DataServerInitializer extends ChannelInitializer {
@Override
public void initChannel(Channel channel) {
channel.pipeline().addLast(“handler”, new DataServerHandler());
}
}


如果我们不想每一个channel都使用一个ChannelHandler去进行处理,则需要使用AttributeKey去保存状态,并且在ChannelHandler上面加上@Sharable的注释。
下面介绍ChannelHandlerContext的使用:
ChannelHandlerContext提供了一个ChannelHandler,通过ChannelHandlerContext,ChannelHandler可以与属于它的ChannelPipeline进行联系。通过ChannelHandlerContext可以让事件往上走或者往下走,也可以动态的修改ChannelPipeline,或者存放信息通过(AttributeKeys)

上面是服务端代码的编写,介绍了一些类的使用,接下来,如何往服务端发送消息并且接收服务端发送的消息,这需要我们编写一个客户端,这样就能够实现交互了。

下面是客户端的主要代码:

    EventLoopGroup group = new NioEventLoopGroup();
    try {
        Bootstrap b = new Bootstrap();
        b.group(group)
         .channel(NioSocketChannel.class)
         .handler(new FactorialClientInitializer(sslCtx));
        // Make a new connection.
        ChannelFuture f = b.connect(HOST, PORT).sync();
        // Get the handler instance to retrieve the answer.
        FactorialClientHandler handler =
            (FactorialClientHandler) f.channel().pipeline().last();
        // Print out the answer.
        System.err.format("Factorial of %,d is: %,d", COUNT, handler.getFactorial());
    } finally {
        group.shutdownGracefully();
    }

上面是客户端的代码,基本和服务端没有什么区别。但要记得这样就算是启动了一个客户端连接到一个服务端,即客户端的Channel已经是连接上了,可以获取的到这个Channel进行操作。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值