netty4 ServerBootstrap.bind(port) debug

本文详细解析了Netty4中ServerBootstrap.bind(port)方法的执行流程,包括Channel的初始化配置、EventLoop的注册及作用、handler的bind方法调用等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码是netty4自带的例子

我们在bind的行前加个断电,下面是ServerBootstrap.bind(port)方法所经历的一些步骤。

 

在AbstractBootstrap.initAndRegister()中实例化一个Channel并进行初始化配置。

(可以参考《Netty4 initAndRegister 解析》)

配置项为在初始化Bootstrap时我们传入的handlers和一些设置。

下面进行channel的初始化(可以参考《Netty 源码分析之 一 揭开 Bootstrap 神秘的红盖头 (客户端)》)

注意箭头所示,这里的channelFactory是ReflectiveChannelFacotry.

这里就返回一个NioServerSocketChannel类型的channel.

下面的init(channel)对刚生成的channel进行一些初始化配置。

接着往下看:

注意下面注释内容。这里有两个添加handler的动作,后面一个时延时的,为了确保所有的handler都被放置在ServerBootstrapAccepter之前。这时候就已经初始化完channle中的eventloop对象了,可以使用eventloop来执行该channel的一些工作。

初始化完channel之后:反回到initAndRegister()先看一下eventloop注册到channel。

EventLoop注册Channel

 

说白了就是获取一个线程来处理这个注册事件。

 

 

 

那么这个EventExecutor到底是什么?我们获得了这个EventExecutor之后我们进入它的register(channel)方法看看:

 

这里注意到,eventloop注册channel实际上是channel注册eventoloop。最终将当前的eventloop传入到channel中作为channel的属性。

 

这里返回unsafe对象。其实 unsafe 特别关键, 它封装了对 Java 底层 Socket 的操作, 因此实际上是沟通 Netty 上层和 Java 底层的重要的桥梁

 

下面的eventLoop.inEvenLoop()判断该eventloop线程是否当前线程。这里关于eventloop的内容要以《Netty4 initAndRegister 解析》为主,下面的内容总结的一般。

然后下面的doregister0调用的是AbstractChannel在子类中的实现。比如当前是NioServerSocketChannel.doBind()同样也是在这个子类中实现的。

 

下面这快是对EventLoop接口的分析。

最后我们进入这个loop线程的执行线程:

先到此为止我们继续上面的逻辑:

以上都是initAndRegister()方法中的逻辑。

接着往下走:

DefaultChannelPipeline{(LoggingHandler#0 = io.netty.handler.logging.LoggingHandler), (ServerBootstrap$ServerBootstrapAcceptor#0 = io.netty.bootstrap.ServerBootstrap$ServerBootstrapAcceptor)}

 

继续主流程:

这里同时要留意eventloop的用法。

Netty内部都是通过线程在处理各种数据,EventLoopGroup就是用来管理调度他们的,注册Channel,管理他们的生命周期,下面就来看看EventLoopGroup是怎样工作的。

EventLoops的目的是为Channel处理IO操作,一个EventLoop可以为多个Channel服务。
EventLoopGroup会包含多个EventLoop。
参考:http://www.tuicool.com/articles/mEJvYb

最终还是到channel.bind(...)这个方法来绑定。

下面进入:final AbstractChannelHandlerContext next = findContextOutbound();

 

EventExecutor executor = next.executor();

 

前面将eventloop注册到channel中之后,这里取出channel的eventloop。

 

 AbstractChannelHandlerContext这个东西有专门博文。

一直循环到ctx.outbound为true的时候,获取这个ctx。

 

 

 

还记得那个上行下行的图吗,这就是那里的感念。

下面进入到EventExecutor executor = next.executor();方法:有上图可知当前的executor属性为null。

我们进入channel().eventloop().这个地方需要返回一个EventExecutor类型的对象。

我们先看channel方法:

pipeline=DefaultChannelPipeline{(LoggingHandler#0 = io.netty.handler.logging.LoggingHandler), (ServerBootstrap$ServerBootstrapAcceptor#0 = io.netty.bootstrap.ServerBootstrap$ServerBootstrapAcceptor)}

这里的(LoggingHandler#0 = io.netty.handler.logging.LoggingHandler)是什么鬼?

还记得上面的init(channel)吗,我们在那里添加了handler和ServerBootstrapAccepter

下面我们还会涉及到这个handler。

 

先不管我们先进入pipeline.channel();方法

return channel().eventLoop();这里执行完channel()方法后我们进入eventloop()方法:
这里需要返回一个EventExecutor类型的对象。

然而这里返回的却是一个NioEventLoop类型的对象。我们在看一下关系图:

先不管我们接着看:下面进入父类的方法:

最终我们执行完了这个方法,返回一个EventLoop类型的对象。

NioEventLoop类型的对象怎么会是EventExecutor类型呢?

通过NioEventLoop类型检索我们发现

原来EventLoop继承了EventExecutor接口。没办法社区办idea功能比较简陋,就不画类型关系图了。

 

那么我们接着netty流程往下走:

 

在下面这个方法这里循环所有的handler的bind()方法。

 

 

 

这里的handler:LoggingHandler。还记得上面的疑问吗。答案在这里。

 

 当前的handler是LoggingHandler那么我们就进入到:

这里又一个非常值得注意的地方。LoggingHandler的bind()方法并没有写什么业务逻辑,只打印了个日志:

18:33:33.694 [nioEventLoopGroup-2-1] INFO  i.n.handler.logging.LoggingHandler - [id: 0x62c8b900] BIND: 0.0.0.0/0.0.0.0:8007

然后直接调用了下一个handler的bind()方法。

这个bind()方法是定义在ChannelOutboundHandler中的方法。

所有我们就行入到下一次循环中了:

开始循环了。

 

 下一个handler:

 到这里终于跳出netty框架到老nio层了!

但是到这的时候程序没进入bind直接跳出来了:

这里成功了?

 

 我们专门打个断点到那个nio bind的地方。根据以上的流程分析我们知道最终的nio层bind就在我们非常熟悉的NioServerSocketChannel中实现。

我们手动进入这个类中找到bind方法:

这就和我们平时直接用nio写业务时一样了:

 

转载于:https://www.cnblogs.com/guazi/p/6586252.html

### 使用 `ServerBootstrap` 绑定端口 8002 在 Netty 中,`ServerBootstrap` 主要用于启动服务端应用程序并监听指定的端口号。为了使服务器能够接收来自客户端的连接请求,必须通过调用 `bind()` 方法来绑定特定的本地地址和端口。 以下是具体的操作方式: #### 创建并配置 `ServerBootstrap` ```java EventLoopGroup bossGroup = new NioEventLoopGroup(); // 负责处理连接事件 EventLoopGroup workerGroup = new NioEventLoopGroup(); // 处理网络通信读写操作 try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // 指定使用的通道类型 .childHandler(new ChannelInitializer<SocketChannel>() { // 配置管道处理器链 @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); p.addLast(new StringDecoder()); p.addLast(new StringEncoder()); p.addLast(new SimpleChannelInboundHandler<String>() { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("Received message: " + msg); ctx.writeAndFlush("Echo: " + msg); } }); } }) .option(ChannelOption.SO_BACKLOG, 128) // 设置TCP参数选项 .childOption(ChannelOption.SO_KEEPALIVE, true); // 子链接设置保持活动状态 ``` 这段代码初始化了一个新的 `ServerBootstrap` 实例,并设置了两个线程池 (`bossGroup`, `workerGroup`) 来分别管理接受新连接的任务以及已建立连接的数据传输任务[^1]。 #### 执行绑定操作 当所有的准备工作完成后,可以执行实际的绑定动作: ```java // 将服务器绑定到 localhost 的 8002 端口上 ChannelFuture f = b.bind("localhost", 8002).sync(); System.out.println("Server started and listening on port 8002"); f.channel().closeFuture().sync(); // 等待直到服务器套接字被关闭 } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } ``` 上述代码片段展示了如何让服务器监听 `localhost` 上的 `8002` 端口。一旦成功绑定了端口,则会打印一条消息表示服务器已经准备好接收传入连接;最后一步是等待程序结束前释放资源[^3]。 对于底层实现细节,在 `NioServerSocketChannel#doBind` 方法内部实现了真正的绑定逻辑,它依赖于 Java 版本的不同而有所差异。如果当前运行环境支持 JDK7 及以上版本的话,那么就会直接调用 `javaChannel().bind(localAddress, config.getBacklog())`; 否则将会采用旧版 API 进行兼容性适配[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值