文章目录

  • 1. AbstractBootstrap类简介
    • 1.1. 核心方法
  • 2. netty服务端创建
    • 2.1. 服务端启动入口
    • 2.2. doBind()方法
    • 2.3. netty服务初始化
      • 2.3.1. initAndRegister()方法
      • 2.3.2. init()方法
      • 2.3.3. ServerBootstrapAcceptor作用
      • 2.3.4. register()方法
        • netty线程安全的特性
        • register0方法
    • 2.4. nety服务端口绑定
      • 2.4.1 doBind0()方法
      • 2.4.2 bind()方法
      • 2.4.3. fireChannelActive()
  • 3. netty客户端连接
    • 3.1. netty客户端入口
    • 3.2. doConnect()方法
    • 3.3 doConnect0()
    • 3.4. unsafe.connect
    • 3.5. NioSocketChannel.doConnect()方法
  • 4、netty一次请求响应过程总结

​ 在之前的文章中我们学习了 netty的io模型、netty的使用分析、netty线程模型、netty的ChannelPipeline类学习、netty的ChannelPromise类学习。下面我们从使用netty例子出发探究一下netty的工作流程(其中包括服务端和客户端的创建、一次完整的请求和响应)。

1. AbstractBootstrap类简介

在探究netty一次请求的完整流程之前我们先来了解一下XXXBootstrap组件。 它是netty为我们提供的便于我们使用的工具启动类。他以builder的模式让我们可以简单设置例如channel、TCP参数、相关处理器。其中客户端使用Bootstrap类,服务端使用ServerBootstrap类两者的基类为AbstractBootstrap类。

1.1. 核心方法

AbstractBootstrap的

  • bind():netty服务端的与端口绑定,也是建立netty服务端的核心方法

Bootstrap的

  • connect():netty客户端与服务端进行连接,也是建立netty客户端的核心方法

补充说明:netty服务底层是使用NIO原理实现的,所以此处分析netty相关源码我们也分析到NIO层,至于NIO的底层原理实现请期待笔者后续的博文。

2. netty服务端创建

2.1. 服务端启动入口

​ netty服务端创建相关代码如下,服务端创建是使用bind()核心方法,下面来分析bind方法

//一、netty 服务端启动
//.......省略ServerBootstrap构建过程
//绑定端口 netty服务端创建
ChannelFuture sync = serverBootstrap.bind(port).sync();
//服务监听端口关闭
sync.channel().closeFuture().sync();

bind()方法最终调用io.netty.bootstrap.AbstractBootstrap#doBind()方法,下面我们来分析netty服务端启动方法。

2.2. doBind()方法

private ChannelFuture doBind(final SocketAddress localAddress) {//正如其方法名一样 初始化channel,并进行channel和selector(多路复用器的注册绑定)//其返回值是netty自己封装的异步结果接口ChannelFuturefinal ChannelFuture regFuture = initAndRegister();final Channel channel = regFuture.channel();//如果异步结果中有异常则直接返回 此时:netty服务端初始化失败if (regFuture.cause() != null) {return regFuture;}final ChannelPromise promise;//上述的initAndRegister的结果是异步通知的 所有此处需要进行条件分支判断//如果上述操作已经完成(通道初始化完成)则直接进行doBind0()方法if (regFuture.isDone()) {promise = channel.newPromise();//channel和端口进行绑定操作doBind0(regFuture, channel, localAddress, promise);} else {//一般情况下initAndRegister会很快完成并给出结果 但是万一呢?promise = new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE);//此处针对如果上述未完成 添加一个事件监听,待上述操作完成后触发doBind0()regFuture.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {doBind0(regFuture, channel, localAddress, promise);}});}return promise;
}

netty服务端启动和绑定过程并不复杂主要分成了两部分(两个核心方法)

  • 初始化: initAndRegister方法,该方法主要功能是创建Channel对象(网络请求的数据流通道),并整合ServerBootstrap相关配置比如工作线程(NioEventLoopGroup)、option(参数配置)、attr(属性)、添加相关处理器(ChannelHandler),最后将多路复用器Selector注册到Channel。
  • 端口绑定:doBind0方法,该方法主要将端口和channel进行绑定。

2.3. netty服务初始化

2.3.1. initAndRegister()方法

final ChannelFuture initAndRegister() {Channel channel;try {//1、创建网络通道Channelchannel = createChannel();} catch (Throwable t) {return VoidChannel.INSTANCE.newFailedFuture(t);}try {//2、为channel配置 线程、option、attr、事件处理器ChannelHandlerinit(channel);} catch (Throwable t) {channel.unsafe().closeForcibly();return channel.newFailedFuture(t);}ChannelPromise regFuture = channel.newPromise();//channel注册多路复用器selectorchannel.unsafe().register(regFuture);if (regFuture.cause() != null) {if (channel.isRegistered()) {channel.close();} else {channel.unsafe().closeForcibly();}}return regFuture;
}

netty服务端初始化也可以分成三部分

  1. 创建netty的channel对象,因为我们在构建的ServerBootstrap对象的时候调用了channel方法传入NioServerSocketChannel.class
//调用channel(NioServerSocketChannel.class)方法 会创建ServerBootstrapChannelFactory
return channelFactory(new ServerBootstrapChannelFactory<ServerChannel>(channelClass));

createChannel方法调用ServerBootstrapChannelFactory的newChannel(),通过反射调用NioServerSocketChannel的构造函数

//构建函数
public NioServerSocketChannel(EventLoop eventLoop, EventLoopGroup childGroup) {//父构造函数 newSocket()创建一个NIO的ServerSocketChannel 和eventLoop线程以及工作线程childGroup//包装成NioServerSocketChannelsuper(null, eventLoop, childGroup, newSocket(), SelectionKey.OP_ACCEPT);config = new DefaultServerSocketChannelConfig(this, javaChannel().socket());
}
  1. 初始化channel配置:init()方法整合ServerBootstrap相关配置比如工作线程(NioEventLoopGroup)、option(参数配置)、attr(属性)、添加相关处理器(ChannelHandler)并配置childHandler、currentChildOptions、currentChildAttrs包装成ServerBootstrapAcceptor
  2. channel注册selector:通过channel的unsafe对象(io.netty.channel.AbstractChannel.AbstractUnsafe)的register方法。

2.3.2. init()方法

​ 在上述方法的第二部分init()方法初始化channel配置最终实现类为io.netty.bootstrap.ServerBootstrap#init。

void init(Channel channel) throws Exception {//获取AbstractBootstrap的options(TCP配置信息) 设置到ChannelConfig中final Map<ChannelOption<?>, Object> options = options();synchronized (options) {channel.config().setOptions(options);}//获取AbstractBootstrap的attr属性 设置到Attribute中final Map<AttributeKey<?>, Object> attrs = attrs();synchronized (attrs) {for (Map.Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {@SuppressWarnings("unchecked")AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();channel.attr(key).set(e.getValue());}}//获取AbstractBootstrap的ChannelHandler 添加到ChannelHandlerPipline中ChannelPipeline p = channel.pipeline();if (handler() != null) {p.addLast(handler());}//子类型的Handler、options、attrs (用户一般使用会创建ChannelInitializer 并通过initChannel// 添加自定义的ChannelHandler)final ChannelHandler currentChildHandler = childHandler;final Map.Entry<ChannelOption<?>, Object>[] currentChildOptions;final Map.Entry<AttributeKey<?>, Object>[] currentChildAttrs;synchronized (childOptions) {currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));}synchronized (childAttrs) {currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));}//将在ServerBootstrap通过childHandler()、childOptions()、childAttrs()等方法// childHandler、currentChildOptions、currentChildAttrs 包装成ServerBootstrapAcceptor使用p.addLast(new ChannelInitializer<Channel>() {@Overridepublic void initChannel(Channel ch) throws Exception {//ServerBootstrapAcceptorch.pipeline().addLast(new ServerBootstrapAcceptor(currentChildHandler, currentChildOptions,currentChildAttrs));}});
}

init()方法的分析可以分成如下部分,

  1. options:通过ServerBootstrap设置的options添加到channel的ChannelConfig实例中(DefaultServerSocketChannelConfig实现类在createChannel方法中实现的)

  2. attrs:通过ServerBootstrap设置的attrs添加到channel的Attribute中。

  3. handler:过ServerBootstrap设置的handler添加到ChannelHandlerPipline中。

  4. 为通道添加一个ChannelInitializer实现类,该类是一个继承ChannelHandlerAdapter的抽象类,其只重写了channelRegistered事件,当channel通过后面的方法 channel.unsafe().register(regFuture) 注册后会触发其中的channelRegistered方法。

  5. 当前的channel进行注册成功后会将childHandler、currentChildOptions、currentChildAttrs 包装成ServerBootstrapAcceptor使用,ServerBootstrapAcceptor是一个特殊的ChannelHandler,该类也是继承了ChannelHandlerAdapter,其重写了channelRead事件,每当有一个客户端连接进来后,会先进入到ServerBootstrapAcceptor的channelRead()。

    补充: channelRegistered事件:channel和selector注册后会触发该方法

    ​ channelRead事件:每当有一个客户端连接进来后,会先进入到ServerBootstrapAcceptor的channelRead()

    上述的4和5步骤我们需要了解ChannelInitializer实现,同时对ServerBootstrapAcceptor类作用进行介绍。

2.3.3. ServerBootstrapAcceptor作用

 p.addLast(new ChannelInitializer<Channel>() {@Overridepublic void initChannel(Channel ch) throws Exception {//Channel添加ServerBootstrapAcceptorch.pipeline().addLast(new ServerBootstrapAcceptor(currentChildHandler, currentChildOptions,currentChildAttrs));}});

​ 在上述init()方法中最终在ChannelPipline通过addLast方法 添加了一个匿名实现ChannelInitializer

public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {ChannelPipeline pipeline = ctx.pipeline();boolean success = false;try {//提供的抽象方法 用于自定义实现initChannel((C) ctx.channel());//将其从ChannelPipline中移除pipeline.remove(this);//将channelRegistered事件透传到别的ChannelHandlerctx.fireChannelRegistered();success = true;} catch (Throwable t) {//异常处理} finally {}
}

ChannelInitializer类的作用是处理channelRegistered事件,主要是执行initChannel,然后移除本匿名ChannelInitializer实现类。结合上述netty源码,这里就是往pipline中添加ServerBootstrapAcceptor对象同时移除这里的ChannelInitializer实现类。

在之前的博文中netty的 线程模型 Reactor多线程模型中 netty会通过注册一个Acceptor事件处理器到mainReactor中,Acceptor事件处理器所关注的事件是ACCEPT事件,这样mainReactor会监听客户端向服务器端发起的连接请求事件(ACCEPT事件),Acceptor会将客户端的I/O事件分发到sub Reactor线程池。 ServerBootstrapAcceptor对象的作用是那个所谓的Acceptor事件处理器,其重写了channelRead方法。

//该方法触发条件是 每一个客户端与服务端建立连接后都会调用该方法
public void channelRead(ChannelHandlerContext ctx, Object msg) {//msg是客户端连接服务端创建的Channel(NioSocketChannel) 强转为ChannelChannel child = (Channel) msg;//为ChannelPipeline添加childHandler(通过ServerBootstrap的childHandler()方法设置的)child.pipeline().addLast(childHandler);//childOptions配置到客户端Channel的ChannelConfig中for (Entry<ChannelOption<?>, Object> e: childOptions) {try {if (!child.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {logger.warn("Unknown channel option: " + e);}} catch (Throwable t) {logger.warn("Failed to set a channel option: " + child, t);}}//childOptions配置到客户端Channel的attr中for (Entry<AttributeKey<?>, Object> e: childAttrs) {child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());}//客户端Channel进行注册 child.unsafe().register(child.newPromise());
}

这个方法和我们上面的init方法的Channel的注册逻辑相似。这里需要补充说明一下:Channel注册分为ServerSocketChannel和SockerChannel注册,服务端启动的时候会先进行ServerSocketChannel的注册,启动完成后,每当有一个客户端连接过来,会进行一次SocketChannel注册。所以之前的init方法注册的是netty服务端的channel而这里的注册是netty客户端的channel。

2.3.4. register()方法

下面我们继续来分析initAndRegister方法的第三部分注册,其调用不是由channel直接调用而是使用channel的工具类Unsafe类的register()方法去执行真正的注册逻辑。

public final void register(final ChannelPromise promise) {//netty使用这种机制保证线程安全//如果调用和eventloop是同一个线程 则直接调用if (eventLoop.inEventLoop()) {register0(promise);} else {try {//否则将调用包装成任务交由eventloop  eventLoop.execute(new Runnable() {@Overridepublic void run() {register0(promise);}});} catch (Throwable t) {//异常处理}}
}
netty线程安全的特性

1.一个EventLoopGroup当中包含一个或多个EventLoop
2.一个EventLoop在其生命周期内只和唯一的一个Thread线程绑定(即一个io线程)
3.所有由EventLoop处理的各种io事件都将在其所关联的io线程上执行,因为是单线程保证了线程安全
4.一个Channel在其生命周期内只会注册在一个EventLoop(selector)上
5.运行期间,一个EventLoop会被分配给一个或多个channel

register0方法
private void register0(ChannelPromise promise) {try {//注册之前确保channel通道是打开状态if (!ensureOpen(promise)) {return;}//调用底层NIO的将Channel注册到selector(多路复用器)doRegister();registered = true;promise.setSuccess();//将channelRegistered事件透传(事件往后传递让别的对该事件感兴趣的// ChannelHandler处理器进行处理) (这里我们所有实现了channelRegistered都有可能接受到该事件 调用自己initChannel方法)pipeline.fireChannelRegistered();//javaChannel().socket().isBound()//isBound() 方法判断 ServerSocket 是否已经与一个端口绑定 此时应该没有绑定if (isActive()) {//产生一个channelActive事件 放入pipeline中pipeline.fireChannelActive();}} catch (Throwable t) {//异常处理}
}

该方法主要分成三个部分

  1. 调用java底层的NIO的api将channel注册到eventLoop的selector(一个selector可以轮询处理多个线程,所以一个eventLoop可以和多个channel进行绑定),eventLoop的selector是创建EventLoopGroup的子EventLoop初始化的。 ops为0表明此处只简单的注册不对任何事件感兴趣。

  2. fireChannelRegistered :将channelRegistered事件透传(事件往后传递让别的对该事件感兴趣的ChannelHandler处理器进行处理)。

  3. ServerSocket 如果和一个端口绑定了就产生一个channelActive事件 放入pipeline中。

2.4. nety服务端口绑定

2.4.1 doBind0()方法

private static void doBind0(final ChannelFuture regFuture, final Channel channel,final SocketAddress localAddress, final ChannelPromise promise) {//在触发channelRegistered()之前调用此方法。给用户处理程序一个设置的机会 channel.eventLoop().execute(new Runnable() {@Overridepublic void run() {//上述的initAndRegister()方法处理完成且成功if (regFuture.isSuccess()) {//服务端Channel进行端口绑定,并添加异步结果监听(绑定结果失败的话关闭channel)channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);} else {//失败设置失败信息promise.setFailure(regFuture.cause());}}});
}

不解释,直接看channel的bind方法

2.4.2 bind()方法

//调用pipeline的bind方法
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {return pipeline.bind(localAddress, promise);
}
//调用pipeline管理的chanelHandler列表
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {return tail.bind(localAddress, promise);
}
//从tail往前查找对bind事件刚兴趣的ChannelHandler
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {DefaultChannelHandlerContext next = findContextOutbound(MASK_BIND);next.invoker.invokeBind(next, localAddress, promise);return promise;
}//通过调用其运行环境invoker中的invokeBindNow方法ctx.handler().bind(ctx, localAddress, promise);//最终查找到header
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)throws Exception {unsafe.bind(localAddress, promise);
}

调用链如下:

channel.bind()-->pipeline.bind()-->next.invoker.invokeBind()-->header.bind()
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {//确保if (!ensureOpen(promise)) {return;}//端口绑定之前 wasActive 为false  //调用方法isBound() 方法判断 ServerSocket 是否已经与一个端口绑定boolean wasActive = isActive();try {//最终调用java原生NIO api进行端口绑定doBind(localAddress);} catch (Throwable t) {}//端口绑定后isActive() 为true 则此时产生一个channelActive事件if (!wasActive && isActive()) {invokeLater(new Runnable() {@Overridepublic void run() {//产生channelActive事件放入管道 同时selector监听ON_ACCEPT事件pipeline.fireChannelActive();}});}//成功异步操作标识promise.setSuccess();
}

两部分

  • 调用java原生NIO api进行端口绑定。
  • 产生channelActive事件放入管道。同时该方法还会给多路复用器添加一个ON_ACCEPT事件,服务端准备就绪可以接收客户端的连接请求。

2.4.3. fireChannelActive()

​ 上述服务端创建过程以经接近尾声,channel完成了初始化、同时注册了多路复用器(监听的事件是0)不对任何事件感兴趣,这里是有点疑问,后续客户端连接和读写事件怎么处理?别着急这里为多路复用器添加感兴趣的事件在fireChannelActive方法中 下面我们来看看该方法逻辑

public ChannelPipeline fireChannelActive() {//触发pipeline中ChannelHandler的channelActive事件head.fireChannelActive();//调用chanel的read(autoRead属性默认为true)if (channel.config().isAutoRead()) {//调用链如下://  channel.read()//      -----> pipeline.read//          ------>tail.read() 从tail往上查找符合MASK_READ的ChannelHandler 这里找到的是headHandler//             ------>unsafe.beginRead();  调用channel的unsafe//。             ------> unsafe.doBeginRead();找到最终调用,我们分析一下该方法channel.read();}return this;
}
protected void doBeginRead() throws Exception {if (inputShutdown) {return;}//获取当前channel感兴趣的SelectionKey(java的NIO四种事件集合)final SelectionKey selectionKey = this.selectionKey;if (!selectionKey.isValid()) {return;}final int interestOps = selectionKey.interestOps();//readInterestOp是该channel在初始化阶段传入的ON_ACCEPT事件//所以此处将channel的interestOps为0 添加监听ON_ACCEPT事件if ((interestOps & readInterestOp) == 0) {selectionKey.interestOps(interestOps | readInterestOp);}

netty服务端经历了Channel的初始化、注册多路复用器、绑定端口、添加对客户端连接事件的监听分析到这里有关netty服务端创建过程完成。贴一个完成的流程图

3. netty客户端连接

3.1. netty客户端入口

netty客户端创建相关代码如下,服务端创建是使用bind()核心方法,下面来分析bind方法

//一、netty 客户端启动
//.......省略Bootstrap构建过程
//发起异步连接请求
ChannelFuture sync = bootstrap.connect(host,port).sync();
//客户端监听端口关闭
sync.channel().closeFuture().sync();

connect()方法最终调用io.netty.bootstrap.AbstractBootstrap#doConnect()方法,下面我们来分析netty客户端连接方法。其实netty客户端建立连接的过程和netty服务端启动过程类似,这里我们也只对区别说明。

3.2. doConnect()方法

private ChannelFuture doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {//netty客户端初始化类似netty服务端类似 final ChannelFuture regFuture = initAndRegister();final Channel channel = regFuture.channel();if (regFuture.cause() != null) {return regFuture;}final ChannelPromise promise = channel.newPromise();//此处不再是doBind0方法而是doConnect0方法if (regFuture.isDone()) {doConnect0(regFuture, channel, remoteAddress, localAddress, promise);} else {regFuture.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {doConnect0(regFuture, channel, remoteAddress, localAddress, promise);}});}return promise;
}

netty服务端启动和绑定过程并不复杂主要分成了两部分(两个核心方法)

  • 初始化: initAndRegister方法,该方法主要功能是创建Channel对象(网络请求的数据流通道),

    1. createChannel方法netty客户端是通过BootstrapChannelFactory对象创建的是NioSocketChannel实例,netty服务端是通过ServerBootstrapChannelFactory对象创建NioServerSocketChannel。

    2. init(Bootstrap实现)方法只是添加handler、options、attr,netty服务端init(ServerBootstrap) 还会将child相关配置包装成ServerBootstrapAcceptor。

    3. register0实现方式两者一致

  • 连接建立:doConnect0方法,该方法绑定客户端channel并和netty服务端建立连接。着重描述。

3.3 doConnect0()

private static void doConnect0(final ChannelFuture regFuture, final Channel channel,final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {channel.eventLoop().execute(new Runnable() {@Overridepublic void run() {if (regFuture.isSuccess()) {//客户端连接建立if (localAddress == null) {channel.connect(remoteAddress, promise);} else {channel.connect(remoteAddress, localAddress, promise);}//连接建立失败监听 关键channelpromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);} else {promise.setFailure(regFuture.cause());}}});
}

调用链如下:

pipeline.connect(remoteAddress, localAddress, promise)--------->tail.connect(remoteAddress, localAddress, promise)---------->findContextOutbound //从tail往前查找对connect感兴趣的HeadHandler---------->unsafe.connect(remoteAddress, localAddress, promise) //最终调用unsafe.connect

3.4. unsafe.connect

​ connect实际调用的是AbstractNioUnsafe的connect方法

public void connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {if (!ensureOpen(promise)) {return;}try {if (connectPromise != null) {throw new IllegalStateException("connection attempt already made");}boolean wasActive = isActive();//调用Java原生API 与服务端建立连接。//连接立即建立的话直接调用fulfillConnectPromise(作用是连接建立完成触发channelActive和channel的read方法,//表示通道中已经有了可读的数据,可以执行读操作了)if (doConnect(remoteAddress, localAddress)) {fulfillConnectPromise(promise, wasActive);} else {//否则异步处理连接请求connectPromise = promise;requestedRemoteAddress = remoteAddress;//如果设置了超时事件则创建一个延时任务(延时事件即为超时时间,里面用来处理超时连接的问题)int connectTimeoutMillis = config().getConnectTimeoutMillis();if (connectTimeoutMillis > 0) {connectTimeoutFuture = eventLoop().schedule(new Runnable() {@Overridepublic void run() {ChannelPromise connectPromise = AbstractNioChannel.this.connectPromise;ConnectTimeoutException cause =new ConnectTimeoutException("connection timed out: " + remoteAddress);if (connectPromise != null && connectPromise.tryFailure(cause)) {close(voidPromise());}}}, connectTimeoutMillis, TimeUnit.MILLISECONDS);}//注册一个连接完成的监听 如果完成说明异步连接成功 则取消之前超时处理延迟任务promise.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {if (future.isCancelled()) {if (connectTimeoutFuture != null) {connectTimeoutFuture.cancel(false);}connectPromise = null;close(voidPromise());}}});}} catch (Throwable t) {//省略异常处理   }
}

方法逻辑如下:

   1. doConnect 调用原生java API 进行和服务端建立连接2. 连接立即建立:接着调用fulfillConnectPromise 触发客户端的读请求 发送数据到channel3. 连接异步建立:设置超时任务处理连接(连接成功后该Channel注册在NioEventLoop上selector会轮循所有的事件进行处理)可以进行后续的读请求处理。

3.5. NioSocketChannel.doConnect()方法

@Override
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {if (localAddress != null) {//调用原生java NIO 客户端socket绑定端口javaChannel().socket().bind(localAddress);}boolean success = false;try {//调用原生java NIO 客户端socket发起服务端建立连接 //此时客户端会在服务端channel ON_ACCEPT事件 此时服务端添加的ServerBootstrapAcceptor对象的//channelRead方法为服务端处理请求添加业务自定义的ChannelHandler。boolean connected = javaChannel().connect(remoteAddress);if (!connected) {//连接建立是异步的//所以此处该channel还需要监听 服务端建立完连接的ON_CONNECT事件selectionKey().interestOps(SelectionKey.OP_CONNECT);}success = true;return connected;} finally {if (!success) {doClose();}}
}

4、netty一次请求响应过程总结

netty源码学习之服务端客户端初始化相关推荐

  1. Netty 源码解析系列-服务端启动流程解析

    netty源码解析系列 Netty 源码解析系列-服务端启动流程解析 Netty 源码解析系列-客户端连接接入及读I/O解析 五分钟就能看懂pipeline模型 -Netty 源码解析 1.服务端启动 ...

  2. 【Netty系列_3】Netty源码分析之服务端channel

    highlight: androidstudio 前言 学习源码要有十足的耐性!越是封装完美的框架,内部就越复杂,源码很深很长!不过要抓住要点分析,实在不行多看几遍,配合debug,去一窥优秀框架的精 ...

  3. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  4. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  5. 视频直播源码中关于服务端直播开播推送实现

    在视频直播源码中直播app开播时需向客户推送开播消息通知用户,实现方式如下: 1.申请相应的推送服务三方,如下使用极光推送,获取相应的配置资料,并做好相应的配置 2.推送代码如下: /* 极光推送 * ...

  6. 菜鸟学源码之Nacos v1.1.3源码学习-Client模块(1):NacosNamingService初始化

    摘要: 本文是Nacos源码学习的第一篇,基于Nacos v1.1.3版本对Nacos源码进行学习,本片主要从exmaple的App示例入手,切入Nacos客户端NacosNamingService的 ...

  7. c语言传奇引擎源码,Ei3.0服务端引擎源代码+登陆网关+客户端插件

    Ei3.0服务端引擎源代码+登陆网关+客户端插件 procedure TPANEL.Button3Click(Sender: TObject); var P:integer; mItem:DWORD; ...

  8. Spring Cloud Eureka 源码分析(一) 服务端启动过程

    2019独角兽企业重金招聘Python工程师标准>>> 一. 前言 我们在使用Spring Cloud Eureka服务发现功能的时候,简单的引入maven依赖,且在项目入口类根据服 ...

  9. 直播软件源码(有服务端+android端+ios端)

    这是我们公司的一个项目:直播软件,是给其他公司开发的,外包项目.现在公司不做了,就想着把这个项目源码分享给爱技术的人. 主播端: 把主播实时录制的视频,经过(采集.美颜处理.编码)推送到服务器 服务器 ...

最新文章

  1. SQL创建表语句文档
  2. 用execSQL语句建表时提示“syntax error”原因及解决方法
  3. osg 改变模型贴图
  4. largest number java_将给定的数组组成最大的数 Largest Number
  5. 已经使用过的rails插件
  6. java se development kit可以卸载吗_首款纯电版MINI COOPER详细评测,或将国产,值得等吗?...
  7. NGUI 必读1 NGUI渲染基础机制
  8. 阿里巴巴 JAVA 开发手册
  9. sqlserver之创建视图
  10. matlab中squareg,matlab中y=square(t,DUTY)的用法
  11. mysql administrator教程_MYSQL administrator 使用
  12. 使用Gitbook写电子书
  13. 使用do…while循环语句计算正数5的阶乘
  14. 客观评价OA的优劣所在
  15. 【代理设置】Linux Windows 系统下各工具设置代理方式笔记(整理中)
  16. ZYNQ PL操作DDR内存读写测试
  17. 微软CRM系统排名?微软CRM系统怎么选?什么是用户口碑最好的微软CRM系统?
  18. LTE无线网络全网优化评估手段
  19. 深度学习:图像检测概述rcnn, fastrcnn, fasterrcnn,yolo,ssd
  20. 一次搞懂 URI、URL、URN

热门文章

  1. urlparse和urlsplit函数的区别 Python学习笔记
  2. 计算机文件管理ppt,计算机中的文件管理及应用研究.ppt
  3. 一个装逼小程序C++代码
  4. 简述osi七层模型和TCP/IP五层模型
  5. 【Python】绘制Karate club空手道图及导出其边信息和节点标签
  6. JavaScript数组添加元素和修改元素方法
  7. 【卡雷尔杯】饮料换购
  8. BLEU算法及其python实现
  9. 苹果6发布时间_苹果又又又要开发布会了,发布时间就在双十一,内容惊喜
  10. 制造业ERP失败原因探秘