[TOC]
# 服务端启动的demo
**Server类**
~~~
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.AttributeKey;
public class Server {
public static void main(String[] args) {
//服务端接收客户端连接的线程,accept
NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
//客户端发送消息给服务端的线程
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//这个里面是空的,什么也不做
ServerBootstrap b = new ServerBootstrap();
//把这2个线程配置进去
b.group(bossGroup, workerGroup)
//设置socketChannel
.channel(NioServerSocketChannel.class)
//设置客户端连接设置tcp的属性
.childOption(ChannelOption.TCP_NODELAY, true)
//每次创建客户端连接的时候绑定些基本属性
.childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
//服务端启动过程中哪些逻辑,这边我写的只是打印出来
//这个handler主要是accept用户的连接
.handler(new ServerHandler())
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//配置数据流读写的处理逻辑.会配置很多
// ch.pipeline().addLast();
//...
}
});
//绑定端口
ChannelFuture f = b.bind(8888).sync();
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
~~~
**ServerHandler类**
~~~
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.concurrent.TimeUnit;
/**
* 主要是accept用户的连接
*/
public class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("channelActive");
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) {
System.out.println("channelRegistered");
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
System.out.println("handlerAdded");
}
@Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
super.channelRead(ctx, msg);
new Thread(new Runnable() {
@Override
public void run() {
// 耗时的操作
String result = loadFromDB();
ctx.channel().writeAndFlush(result);
ctx.executor().schedule(new Runnable() {
@Override
public void run() {
// ...
}
}, 1, TimeUnit.SECONDS);
}
}).start();
}
private String loadFromDB() {
return "hello world!";
}
}
~~~
# 服务端启动流程
* 创建服务端Channel
调用JDK底层API的jdk的channel,把他包装成自己的channel,同时创建一些基本组件绑定在自己的channel上面
* 初始化服务端Channel
初始化一些基本属性,添加逻辑处理器器
* 注册selector
将底层的Channel注册到底层的selector轮询器上面,把服务端channel作为一个daragram绑定在jdk底层的channel上面,事件轮询有数据来的时候直接拿daragram
* 端口绑定
调用jdk底层的API,绑定端口
## 创建服务端Channel
~~~
--bind()[用户代码入口]
|
|--initAndRegister()初始化并注册
|
|--newChannel()[创建服务端channel]
~~~
bind()我们一直跟进去会发现调用doBind()方法
在doBind()方法里面发现有
initAndRegister()这个方法这个就是创建服务端的channel方法
到initAndRegister()这个方法里面这里就是创建服务端channel
~~~
//创建服务端channel
channel = channelFactory.newChannel();
//初始化channel
init(channel);
~~~
这边是newChannel(),我们到ReflectiveChannelFactory这个类里面看
~~~
public T newChannel() {
try {
return clazz.newInstance();
~~~
他是反射创建的,那么我们就要看channelFactory了
这个channelFactory在哪里初始化的,又传了哪些channelFactory?
我们回到业务代码那边的.channel(NioServerSocketChannel.class)点channel进去
~~~
return channelFactory(new ReflectiveChannelFactory(channelClass));
~~~
channelFactory没做什么事情只是把他原路返回
参数channelClass就是传进来的NioServerSocketChannel
看到他会把这个传进来的进行反射创建channel
那么我们现在要看下NioServerSocketChannel做了什么事情
## 反射创建服务端Channel
~~~
newSocket()[通过jdk来创建底层jdk channel]
|
|
NioServerSocketChannelConfig()[tcp参数配置类]
|
|
AbstractNioChannel()
|
|--configureBlocking(false)[阻塞模式]
|
|--AbstractChannel()[创建id,unsafe,pipeline]
~~~
我们来看NioServerSocketChannel源码
~~~
public NioServerSocketChannel() {
this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}
~~~
然后点newSocket进去,发现
~~~
return provider.openServerSocketChannel();
~~~
这就是调用jdk底层的创建ServerSocketChannel
这个NioServerSocketChannel构造方法创建完毕会调用这个
~~~
public NioServerSocketChannel(ServerSocketChannel channel) {
super(null, channel, SelectionKey.OP_ACCEPT);
config = new NioServerSocketChannelConfig(this, javaChannel().socket());
}
~~~
他会把jdk创建好的ServerSocketChannel传递进去,就是这边的形参this
如果以后想对这个channel的tcp参数获取可以用NioServerSocketChannelConfig这个
然后我们看上面一行
~~~
super(null, channel, SelectionKey.OP_ACCEPT);
~~~
super一层层点上去发现AbstractNioChannel里面有这行
~~~
ch.configureBlocking(false);
~~~
这行就是设置服务端的channel是非阻塞的
然后我们看
~~~
AbstractChannel()[创建id,unsafe,pipeline]
~~~
AbstractChannel就是对channel的一个抽象,
服务端,客户端的channel都是继承这个的
这个id就是channel的唯一标识,unsafe是tcp相关的读写,pipeline是处理的逻辑链
~~~
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
~~~
点这个super到上一层,发现
~~~
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
~~~
这些就是设置那几个参数
## 初始化服务端channel
~~~
init()[初始化入口]
|
|--set ChannelOptions,ChannelAttrs
|
|--set ChildOptions, ChildAttrs
|
|--config handler[配置服务端pipeline]
|
|--add ServerBootstrapAcceptor[添加连接器]
~~~
我们从业务层的bind跟进去到dobind方法
然后发现这行
~~~
final ChannelFuture regFuture = initAndRegister();
~~~
initAndRegister里面有个init方法,我们用command+option+b找到实现类
~~~
void init(Channel channel) throws Exception {
final Map, Object> options = options0();
synchronized (options) {
channel.config().setOptions(options);
}
~~~
这就是把我们绑定的channel设置进去,下面也是一样
在这个方法里面在往下看
~~~
synchronized (childOptions) {
currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
}
synchronized (childAttrs) {
currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
}
~~~
这就是把ChildOptions, ChildAttrs 保存到currentChildOptions和currentChildAttrs这2个局部变量
这个方法往下看
~~~
p.addLast
~~~
拿到服务端的pipeline,配置服务端pipeline
~~~
config.handler();
~~~
把用户自定义的handler添加进去
handler就是把他保存为bootstrap的变量,然后在外面
~~~
pipeline.addLast(handler);
~~~
用这样的方法把用户的逻辑处理链添加进去
然后netty会把一个特殊的channelhandle(添加连接器),他会在你启动的时候添加一个特殊的处理器
还是那个方法往下看
~~~
pipeline.addLast(new ServerBootstrapAcceptor(
currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
~~~
在构造ServerBootstrapAcceptor的时候会把前面设置的currentChildOptions和currentChildAttrs和用户代码里面配置的childHandler对应currentChildHandler
currentChildGroup就是用户代码里面的group
# 服务端socket在哪里初始化?
# 在哪里accept连接?