Netty ServerBootstrap如何绑定端口
这篇讲netty服务端ServerBootstrap如何启动
前言
BootStrap在netty的应用程序中负责引导服务器和客户端。netty包含了两种不同类型的引导:
- 使用服务器的ServerBootStrap,用于接受客户端的连接以及为已接受的连接创建子通道。
- 用于客户端的BootStrap,不接受新的连接,并且是在父通道类完成一些操作。
一般服务端的代码如下所示:
public final class SimpleServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new SimpleServerHandler())
.childHandler(new SimpleServerInitializer())
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(8888).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
private static class SimpleServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive");
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelRegistered");
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("handlerAdded");
}
}
public class SimpleServerInitializer extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new SimpleChatServerHandler());
System.out.println("SimpleChatClient:" + ch.remoteAddress()+"连接上");
}
}
在上篇博文(插入上篇文章)中 剖析了如下的两行代码内部的构造函数中干了些什么。
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap如何启动
本篇文章将分析如下几行代码里面做了些什么。
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new SimpleServerHandler())
.childHandler(new SimpleServerInitializer())
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
我们看下构造函数将workerGroup保存在 ServerBootstrap对象的childGroup属性上。 bossGroup保存在ServerBootstrap对象的group属性上
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;
return this;
}
设置父类属性channelFactory 为: BootstrapChannelFactory类的对象。其中这里BootstrapChannelFactory对象中包括一个class属性为:NioServerSocketChannel.class,从如下该类的构造函数中可以明显的得到这一点。
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new BootstrapChannelFactory<C>(channelClass));
}
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return (B) this;
}
并且BootstrapChannelFactory中提供 newChannel()方法,我们可以看到 clazz.newInstance(),主要是通过反射来实例化NioServerSocketChannel.class
rivate static final class BootstrapChannelFactory<T extends Channel> implements ChannelFactory<T> {
private final Class<? extends T> clazz;
BootstrapChannelFactory(Class<? extends T> clazz) {
this.clazz = clazz;
}
@Override
public T newChannel() {
try {
return clazz.newInstance();
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
@Override
public String toString() {
return StringUtil.simpleClassName(clazz) + ".class";
}
}
这里的handler函数的入参类是我们自己提供的。如下,后面的博文中将会分析这个handler将会在哪里以及何时被调用,这里只需要记住这一点即可
public B handler(ChannelHandler handler) {
if (handler == null) {
throw new NullPointerException("handler");
}
this.handler = handler;
return (B) this;
}
由最后一句可知,其实就是讲传入的childHandler赋值给ServerBootstrap的childHandler属性。
该函数的主要作用是设置channelHandler来处理客户端的请求的channel的IO。 这里我们一般都用ChannelInitializer这个类的实例或则继承自这个类的实例
这里我是通过新建类SimpleChatServerInitializer继承自ChannelInitializer。具体的代码如下
public ServerBootstrap childHandler(ChannelHandler childHandler) {
if (childHandler == null) {
throw new NullPointerException("childHandler");
}
this.childHandler = childHandler;
return this;
}
由最后一句可知,其实就是讲传入的childHandler赋值给ServerBootstrap的childHandler属性。
该函数的主要作用是设置channelHandler来处理客户端的请求的channel的IO。 这里我们一般都用ChannelInitializer这个类的实例或则继承自这个类的实例
这里我是通过新建类SimpleChatServerInitializer继承自ChannelInitializer。具体的代码如下:
public class SimpleChatServerInitializer extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new SimpleChatServerHandler());
System.out.println("SimpleChatClient:" + ch.remoteAddress()+"连接上");
}
}
我们再看看ChannelInitializer这个类的继承图可知ChannelInitializer其实就是继承自ChannelHandler的
可知,这个类其实就是往pipeline中添加了很多的channelHandler。
ServerBootstrap的option
这里调用的是父类的AbstractBootstrap的option()方法,源码如下:
public <T> B option(ChannelOption<T> option, T value) {
if (option == null) {
throw new NullPointerException("option");
}
if (value == null) {
synchronized (options) {
options.remove(option);
}
} else {
synchronized (options) {
options.put(option, value);
}
}
return (B) this;
}
其中最重要的一行代码就是:
options.put(option, value);
这里用到了options这个参数,在AbstractBootstrap的定义如下:
private final Map<ChannelOption, Object> options = new LinkedHashMap, Object>();
可知是私有变量,而且是一个Map集合。这个变量主要是设置TCP连接中的一些可选项,而且这些属性是作用于每一个连接到服务器被创建的channel。
ServerBootstrap的childOption
这里调用的是父类的ServerBootstrap的childOption()方法,源码如下:
public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
if (childOption == null) {
throw new NullPointerException("childOption");
}
if (value == null) {
synchronized (childOptions) {
childOptions.remove(childOption);
}
} else {
synchronized (childOptions) {
childOptions.put(childOption, value);
}
}
return this;
}
这个函数功能与option()函数几乎一样,唯一的区别是该属性设定只作用于被acceptor(也就是boss EventLoopGroup)接收之后的channel。到此为止netty的初始化设置完成,下篇文章我们讲一下启动过程。
结束
结束
识别下方二维码!回复:
入群
,扫码加入我们交流群!
最新文章
- 详解 ML2 Core Plugin(I) - 每天5分钟玩转 OpenStack(71)
- centos下安装yaf框架
- HTML <;label>; 标签
- WPF The Hard Way
- js 如何在浏览器中获取当前位置的经纬度
- ubuntu 常用命令集合版(一)【大侠勿喷,菜鸟欢迎】(转载)
- Chart 绘制,自带动画效果
- 支持异步通知的globalfifo平台设备驱动程序及其测试代码
- bzoj 1242: Zju1015 Fishing Net 弦图判定
- Qt入门(9)——Qt中的线程支持
- Python标准库:内置函数dict(**kwarg)
- webapp在Android中点击链接的时候会有淡蓝色的遮罩层
- [C]\x字符转义序列
- http请求在ie中F12查看显示已挂起
- vsftp为不同用户设置不同的ftp的根目录
- 【PAT】B1069 微博转发抽奖(20 分)
- Luogu3163 [CQOI2014]危桥 ---- 网络流 及 一个细节的解释
- ENode, 领域模型,DDD
- CTF SQL注入知识点
- Esper学习之十二:EPL语法(八)
热门文章
- 深入刨析tomcat 之---第2篇,解决第3章bug 页面不显示内容http://localhost:8080/servlet/ModernServlet?userName=zhangyantao&;password=1234
- Couchdb 垂直权限绕过漏洞(CVE-2017-12635)
- lerna 常用命令
- 去除Windows-XP系统下的关机功能
- mybatis中Oracle及mysql插入时自动生成主键以及返回主键
- remote: Support for password authentication was removed
- Mantis安装过程笔记
- Debian 11 “bullseye” 安装笔记
- FTP三种访问模式
- STM32—串口使用总结