在上一篇我们已经介绍了客户端的流程分析,我们已经对启动已经大体上有了一定的认识,现在我们继续看对服务端的流程来看一看到底有什么区别。

服务端代码

public class NioServer {

private static final int PORT = 9898;

public static void main(String[] args) {

EventLoopGroup boss = new NioEventLoopGroup(1);

EventLoopGroup work = new NioEventLoopGroup();

try {

ServerBootstrap bootstrap = new ServerBootstrap();

bootstrap.group(boss, work).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100)

.handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer() {

@Override

protected void initChannel(SocketChannel ch) throws Exception {

ChannelPipeline pipeline = ch.pipeline();

pipeline.addLast("stringDecoder", new StringDecoder());

pipeline.addLast("stringEncoder", new StringEncoder());

pipeline.addLast("serverHandle", new SimpleChannelInboundHandler() {

@Override

protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {

}

});

}

});

ChannelFuture channelFuture = bootstrap.bind(PORT).sync();

channelFuture.channel().closeFuture().sync();

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

boss.shutdownGracefully();

work.shutdownGracefully();

}

}

}

上面代码主要做了以下几件事

声明 EventLoopGroup 不管是服务端还是客户端都需要初始化 EventLoopGroup,上面代码中声明了两个 EventLoopGroup,boss 的作用是处理客户端的连接事件,work 的作用是处理客户端连接事件的 IO 操作。

channel 的初始化,我们发现和客户端的类型不一样,客户端是 NioSocketChannel 而服务端则是 NioServerSocketChannel。

option 的设置。

handler 的设置。

childHandler 的设置。

group 的初始化操作

NioEventLoopGroup 的初始化操作在客户端已经说明过了,这里就不再多说了。我们直接来看 bootstrap.group(boss, work); 这句代码我们跟进去。

public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {

// 调用父类构造器传入 boss

super.group(parentGroup);

if (childGroup == null) {

throw new NullPointerException("childGroup");

}

if (this.childGroup != null) {

throw new IllegalStateException("childGroup set already");

}

// 将 work 赋值给 childGroup 属性

this.childGroup = childGroup;

return this;

}

// 父类的构造器

public B group(EventLoopGroup group) {

if (group == null) {

throw new NullPointerException("group");

}

if (this.group != null) {

throw new IllegalStateException("group set already");

}

this.group = group;

return self();

}

上面主要是把 boss 的引用传入给父类的 group 属性保存,work 给自己的 childGroup 属性保存。boss 主要用于处理客户端的连接请求就像饭店的一个服务员会引导顾客,而 work 就是厨师实际干活的啦。

Channel 的初始化过程

通过客户端的分析我们其实已经知道 bootstrap.channel(class); 方法是将 class 存入到一个 ReflectiveChannelFactory 实例中真正的实例化 channel 就是调用该工厂的 newChannel(); 方法实现。

public B channel(Class extends C> channelClass) {

if (channelClass == null) {

throw new NullPointerException("channelClass");

}

return channelFactory(new ReflectiveChannelFactory(channelClass));

}

// ReflectiveChannelFactory

@Override

public T newChannel() {

try {

return constructor.newInstance();

} catch (Throwable t) {

throw new ChannelException("Unable to create Channel from class " + constructor.getDeclaringClass(), t);

}

}

这样就能创造出一个 NioServerSocket 实例。从代码中可以看出是通过反射实例化的那么肯定是执行 NioServerSocketChannel 的构造方法,我们继续来看下 NioServerSocketChannel 的实例化过程,在看之前我们先来看下 NioServerSocketChannel 的继承结构图。

首先我们来看 NioServerSocket 的构造器

private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();

public NioServerSocketChannel() {

this(newSocket(DEFAULT_SELECTOR_PROVIDER));

}

我们看到构造器发现在内部调用了 newSokcet 方法传了 sun.nio.ch.DefaultSelectorProvider 实例进去。

private static ServerSocketChannel newSocket(SelectorProvider provider) {

try {

// 调用 openSocketChannel 方法获取 ServerSocketChannel

return provider.openServerSocketChannel();

} catch (IOException e) {

throw new ChannelException(

"Failed to open a server socket.", e);

}

}

获取到了 ServerSocketChannel 后会调用重载的构造器

public NioServerSocketChannel(ServerSocketChannel channel) {

super(null, channel, SelectionKey.OP_ACCEPT);

config = new NioServerSocketChannelConfig(this, javaChannel().socket());

}

发现会调用父类的构造器和创造一个 config 实例。我们到 AbstractNioChannel 的构造器看看

protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {

super(parent);

// ch 赋值

this.ch = ch;

// OP_ACCEPT 赋值

this.readInterestOp = readInterestOp;

try {

// 设置非阻塞

ch.configureBlocking(false);

} catch (IOException e) {

try {

ch.close();

} catch (IOException e2) {

if (logger.isWarnEnabled()) {

logger.warn(

"Failed to close a partially initialized socket.", e2);

}

}

throw new ChannelException("Failed to enter non-blocking mode.", e);

}

}

上面代码主要设置了一下 channel 和继续调用父类的构造器

protected AbstractChannel(Channel parent) {

this.parent = parent;

id = newId();

unsafe = newUnsafe();

pipeline = newChannelPipeline();

}

上面代码在客户端的时候已经说过,但是需要注意的是 unsafe 的实例类型会不一样,服务端的实例类型是 AbstractNioMessageChannel$NioMessageUnsafe 的实例。

@Override

protected AbstractNioUnsafe newUnsafe() {

return new NioMessageUnsafe();

}

ChannelPipeline 的实例化

ChannelPipeline 的流程在客户端的分析中说明了,后面也会单独出一期 ChannelPipeline 的说明,在此不在说了。

bootstrap.option()

private final Map, Object> options = new LinkedHashMap, Object>();

public B option(ChannelOption 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 self();

}

我们发现 option 方法中是调用父类的 AbstractBootstrap.option 来为 options 赋值。

bootstrap.handler()

private volatile ChannelHandler handler;

public B handler(ChannelHandler handler) {

if (handler == null) {

throw new NullPointerException("handler");

}

this.handler = handler;

return self();

}

handler() 方法也是调用父类的 AbstractBootstrap.handler() 来为 handler 属性赋值。

bootstrap.childHandler()

private volatile ChannelHandler childHandler;

public ServerBootstrap childHandler(ChannelHandler childHandler) {

if (childHandler == null) {

throw new NullPointerException("childHandler");

}

this.childHandler = childHandler;

return this;

}

childHandler() 方法也是调用自身的 handler() 来为 childHandler 属性赋值。因为这是服务端扩展的一个属性。

bootstrap.bind()

终于到了重头戏了,我们查看了下 bind() 到最后发现是 io.netty.bootstrap.AbstractBootstrap#doBind 中实现的.

private ChannelFuture doBind(final SocketAddress localAddress) {

// 重点查看代码

final ChannelFuture regFuture = initAndRegister();

// 以下代码省略

if (regFuture.cause() != null) {

return regFuture;

}

return promise;

}

}

我们跟进去 initAndRegister() 方法看一看.

final ChannelFuture initAndRegister() {

Channel channel = null;

try {

// channel 初始化在上面已经说过了

channel = channelFactory.newChannel();

// 初始化 channel

init(channel);

} catch (Throwable t) {

}

// 这个 group 就是 bossGroup

ChannelFuture regFuture = config().group().register(channel);

}

我们重点看 init(channel); 在下面获取到的就是 bossGroup 随后就将 bossGroup 和我们的 NioServerSocketChannel 关联起来了。

那么我们的 workGroup 是怎么处理的呢?我们进去看看 init(channel);

@Override

void init(Channel channel) throws Exception {

// 将之前设置的 option 和 NioServerSocketChannel 关联起来

final Map, Object> options = options0();

synchronized (options) {

setChannelOptions(channel, options, logger);

}

// 设置一些属性到 NioServerSocketChannel 中

final Map, Object> attrs = attrs0();

synchronized (attrs) {

for (Entry, Object> e: attrs.entrySet()) {

@SuppressWarnings("unchecked")

AttributeKey key = (AttributeKey) e.getKey();

channel.attr(key).set(e.getValue());

}

}

// 获取到 NioServerSocketChannel 对应的 ChannelPipeline,在实例化的时候就已经创建了对应了 ChannelPipeline

ChannelPipeline p = channel.pipeline();

final EventLoopGroup currentChildGroup = childGroup;

final ChannelHandler currentChildHandler = childHandler;

final Entry, Object>[] currentChildOptions;

final Entry, Object>[] currentChildAttrs;

synchronized (childOptions) {

currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));

}

synchronized (childAttrs) {

currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));

}

// 重点

p.addLast(new ChannelInitializer() {

@Override

public void initChannel(final Channel ch) throws Exception {

final ChannelPipeline pipeline = ch.pipeline();

ChannelHandler handler = config.handler();

if (handler != null) {

pipeline.addLast(handler);

}

ch.eventLoop().execute(new Runnable() {

@Override

public void run() {

pipeline.addLast(new ServerBootstrapAcceptor(

ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));

}

});

}

});

}

上面代码主要做了

设置 option

设置 attr

设置 NioServerSocketChannel 对应的 ChannelPipeline

从上面的代码片段中我们看到, 它为 pipeline 中添加了一个 ChannelInitializer, 而这个 ChannelInitializer 中添加了一个关键的 ServerBootstrapAcceptor handler。

我们先来看下这个类

ServerBootstrapAcceptor(

final Channel channel, EventLoopGroup childGroup, ChannelHandler childHandler,

Entry, Object>[] childOptions, Entry, Object>[] childAttrs) {

this.childGroup = childGroup;

this.childHandler = childHandler;

this.childOptions = childOptions;

this.childAttrs = childAttrs;

enableAutoReadTask = new Runnable() {

@Override

public void run() {

channel.config().setAutoRead(true);

}

};

}

ServerBootstrapAcceptor 这个类中包含了 childGroup、childHandler、childOptions、childAttrs,并且重写了 channelRead 方法。

并且将 ServerBootstrapAcceptor 的实例放在了 ChannelPipeline 的最后面.跟进 addLast() 方法会发现是在 DefaultChannelPipeline.addLast0()

private void addLast0(AbstractChannelHandlerContext newCtx) {

AbstractChannelHandlerContext prev = tail.prev;

newCtx.prev = prev;

newCtx.next = tail;

prev.next = newCtx;

tail.prev = newCtx;

}

从上面代发会发现将 ServerBootstrapAcceptor 放在了最后一个。

Channel 的注册

服务器端和客户端的 Channel 的注册过程一致。

netty 关闭chnnal_Netty 源码学习——服务端流程分析相关推荐

  1. 成品app直播源码,服务端与客户端传输视频文件

    成品app直播源码,服务端与客户端传输视频文件相关的代码 Server端 #define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARN ...

  2. 校园跑腿小程序源码,服务端+客户端,可运营

    需要准备 1.小程序 2.服务器(推荐配置2h4g3m) 3.域名(需要备案) 搭建教程 使用服务器搭建宝塔 安装pm2管理器 新建项目上传服务器接口 修改/pub/config/config.js ...

  3. Android 源码 Camera2 预览流程分析四

    <Android 源码 Camera2 预览流程分析二>中进行了流启动,这是调用 QCamera3Channel start() 方法实现的,对应于 HAL_PIXEL_FORMAT_YC ...

  4. 网游源码完整服务端_【每日源码】微人事是一个前后端分离的人力资源管理系统,项目采用SpringBoot+Vue开发。...

    整体效果 首先,不同的用户在登录成功之后,根据不同的角色,会看到不同的系统菜单,完整菜单如下: 不同用户登录上来之后,可能看到的会有差异,如下: 每个用户的角色是由系统管理员进行分配的,系统管理员给用 ...

  5. android 快传 源码_最新安卓仿茄子快传APP源码包括服务端源码Android开发快传类项目源码全套...

    适使用范围:  安卓APP茄子快传源码 Android项目源码相似茄子快传的快传项目包括服务端 运行环境:  Android app 源码详细: 本项目是一个基于安卓的相似茄子快传的项目源码,包括安卓 ...

  6. 校园服务小程序源代码分享园服务微信小程序全开源版源码-包含服务端

    2021年4月17日更新 严正声明: [请一定勿将程序用户商业用途且 包括 用此程序去参加各类学校的竞赛或者其他以获取名利而参与的竞赛等,一旦被原作者发现将会面临严重的侵权责任后果,特别是被获奖后会遭 ...

  7. ASP.NET版在线客服系统源码 带服务端

    代码分网站,服务端,客端三部份组成. 不是外面那种纯网页的在线客服源码跟这没的比. 演示功能参考www.qchat.cn 转载于:https://www.cnblogs.com/sxsoft/arch ...

  8. 部落战争手游源码( 服务端+客户端+资源+开发文档)

    客户端:cocos2d-x C++开发 服务端:JAVA 数据库:Sql 下载地址:http://www.51xyyx.com/2733.html 下载地址:http://www.51xyyx.com ...

  9. 最新宝可梦回合/口袋Jue醒手游全套源码+Linux服务端

    正文: 完整标题: 宝可梦回合手游[口袋Jue醒]最新整理Linux手工服务端+GM后台+全套源码 无需下载mongodb上传即可,取消客户端显示调试信息 视频安装教程在源码压缩包内 程序: wwtg ...

最新文章

  1. Build a react project step by step
  2. 解决vs2017不能添加引用问题
  3. 用了虚拟机Linux不能上网,虚拟机Linux不能上网怎么办
  4. Linux命令行–基本的bash shell命令
  5. Python JAVA Solutions for Leetcode
  6. Android系统服务-WindowManager
  7. jenkins是什么_使用 Jenkins 自动发布服务到 Kubernetes
  8. Python基础(函数)
  9. air for android 使用ANE来获取安卓手机IMEI号
  10. 利用ComplexHeatmap绘制热图(一)
  11. Moon一个无视Linq,无视实体类的设计思路.(不要错过,看了之后, 让我们从此以后不再羡慕linq to entiy!)...
  12. 零代码实现接口自动化测试-RF框架实践
  13. HTC G14解锁S-OFF、刷机、获取ROOT权限
  14. 利用Cookie进行自动登录
  15. 网页调起支付宝付款和微信付款
  16. LTC咨询LTC培训经验分享:华为系智谋帮许浩明老师辅导上市公司欧菲光科技进行基于LTC流程的能力提升
  17. linux dd iflag oflag,【转】dd命令详解及利用dd测试磁盘性能
  18. 集丰照明|一帖说清6种防眩射灯应用在家装修上
  19. 微服务失败重试(2)AWS 消息服务选型对照表
  20. mid代表计算机,MID是什么

热门文章

  1. 腾讯广告算法大赛 | 萌新粉丝投稿讲述数据竞赛小白观赛心得
  2. socket 获取回传信息_java中使用网络通信(Socket)来传输对象
  3. glide加载gif图不显示动画_关于glide加载gif问题
  4. [译]AppExtension编程指南:扩展基础1
  5. java多线程-生产者消费者模式
  6. [设计模式-创建型]建造者(Builder)
  7. [Axis2与Eclipse整合开发Web Service系列之二] Top-Down方式,通过WSDL逆向生成服务端
  8. 【GWT系列】实现远程过程调用
  9. python票务系统_列车票务系统
  10. android异步工作,Android异步消息机制详解