通过一个简单的例子看服务端的启动过程以及客户端与服务端的连接过程(Netty 4.0.18)

Netty版本:4.0.18

这个例子接受socket连接,并将客户端发送来的数据输出到控制台,不做任何响应。代码来自Netty的Example。

首先看源代码。运行这个例子,然后执行命令telnet localhost 8080 在命令行的任何输入,都将被服务端输出到控制台。

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.ReferenceCountUtil;

public class DiscardServerHandler extends SimpleChannelInboundHandler<Object> {

    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
		ByteBuf in = (ByteBuf) msg;
	    try {
	        while (in.isReadable()) { // (1)
	            System.out.print((char) in.readByte());
	            System.out.flush();
	        }
	    } finally {
	        //ReferenceCountUtil.release(msg); // (2)
	    }
    }

    public void exceptionCaught(ChannelHandlerContext ctx,
            Throwable cause) throws Exception {
        // Close the connection when an exception is raised.
        logger.log(
                Level.WARNING,
                "Unexpected exception from downstream.",
                cause);
        ctx.close();
    }
}
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * Discards any incoming data.
 */
public class DiscardServer {

    private final int port;

    public DiscardServer(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        //bossGroup只用来监听客户端的连接,可用阻塞的方式,所以只需要一个线程,故传入参数1
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        //workerGroup用来处理客户端与服务端的各种IO操作
        try {
            ServerBootstrap b = new ServerBootstrap();//服务端的引导程序
            b.group(bossGroup, workerGroup) //boss将赋给b的父类实例,worker赋给b
             .channel(NioServerSocketChannel.class)//将通过反射机制创建该实例
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ch.pipeline().addLast(new DiscardServerHandler());
                 }
             }); //这里的匿名类的实例将加到每个socketchannel的pipeline中。(每个客户端与服务端连接成功都将产生一个socketchannel)

            // Bind and start to accept incoming connections.
            ChannelFuture f = b.bind(port).sync();
            //netty中所有的IO操作都是异步的,也就是说IO操作会立即返回,返回时不保证操作已经完成
            //通过返回一个ChannelFuture来告诉你IO操作的结果和状态
            //调用channelFuture的sync方法,为阻塞线程,等待结果的出现,该方法被打断时会抛出InterruptedException

            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 8080;
        }
        new DiscardServer(port).run();
    }
}

  • 服务端的启动过程:
程序首先实例化了一个ServerBootstrap,可以看出它的构造器并没有参数,因为参数太多,所以在后面调用了一串链式方法,给它的实例b设置了一系列参数,参数的意义在注释中已经说明。但目前为止并没有实质性的动作,当调用bind方法时,服务端就开始启动了。


调用bind后,首先了调用AbstractBootstrap中的initAndRegister方法,通过反射机制实例化一个NioServerSocketChannel(调用ServerBootstrap.channel方法传入了它的class),代码中变量名为channel,为了防止迷惑,称之为serverSocketChannel。然后初始化serverSocketChannel,设置它的参数(可设置的参数见ChannelOption类),获取它的ChannelPipeline,并添加了一个ChannelHandler(实际上为ServerBootstrapAcceptor的实例,该实例包含了workerGroup,它的handler,以及一些参数),最后向bossGroup注册。返回一个ChannelFuture。到这里启动就结束了。ChannelFuture的解释在注释中。

  • 客户端与服务端的连接过程

当我们使用telnet localhost 8080连接服务端时,NioServerSocketChannel通过调用accept方法完成连接,获得socketChannel。触发channelRead事件,通过回调从serverSocketChannel的ChannelPipeline中选择第一个ChannelHandler(也即head,默认情况下serverSocketChannel也只有这么一个handler,貌似也没有添加多个的必要,毕竟serverSocketChannel只是用来处理连接而已,上文说过,这个head也就是ServerBootstrapAcceptor的实例,这个类是ServerBootstrap的内部类)。然后调用head这个handler的channelRead(ChannelHandlerContext ctx, Object msg)方法。(事实上,这个msg对象就是这个socketChannel),然后获取socketChannel的ChannelPipeline,现在这个管道中是没有任何handler的,随后将我们启动服务端时,调用ServerBootstrap.childChannel()方法传入的handler加入到这个socketChannel的管道中。随后将这个socketChannel向workerGroup注册,到此为止,客户端和服务端连接成功。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值