Netty源码分析第三章: 客户端接入流程
第三节: NioSocketChannel的创建
回到上一小节的read()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 1public void read() {
2 //必须是NioEventLoop方法调用的, 不能通过外部线程调用
3 assert eventLoop().inEventLoop();
4 //服务端channel的config
5 final ChannelConfig config = config();
6 //服务端channel的pipeline
7 final ChannelPipeline pipeline = pipeline();
8 //处理服务端接入的速率
9 final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
10 //设置配置
11 allocHandle.reset(config);
12 boolean closed = false;
13 Throwable exception = null;
14 try {
15 try {
16 do {
17 //创建jdk底层的channel
18 //readBuf用于临时承载读到链接
19 int localRead = doReadMessages(readBuf);
20 if (localRead == 0) {
21 break;
22 }
23 if (localRead < 0) {
24 closed = true;
25 break;
26 }
27 //分配器将读到的链接进行计数
28 allocHandle.incMessagesRead(localRead);
29 //连接数是否超过最大值
30 } while (allocHandle.continueReading());
31 } catch (Throwable t) {
32 exception = t;
33 }
34 int size = readBuf.size();
35 //遍历每一条客户端连接
36 for (int i = 0; i < size; i ++) {
37 readPending = false;
38 //传递事件, 将创建NioSokectChannel进行传递
39 //最终会调用ServerBootstrap的内部类ServerBootstrapAcceptor的channelRead()方法
40 pipeline.fireChannelRead(readBuf.get(i));
41 }
42 readBuf.clear();
43 allocHandle.readComplete();
44 pipeline.fireChannelReadComplete();
45 //代码省略
46 } finally {
47 //代码省略
48 }
49}
50
我们继续剖析int localRead = doReadMessages(readBuf)这一部分逻辑
我们首先看readBuf:
1
2 1private final List<Object> readBuf = new ArrayList<Object>();
2
这里只是简单的定义了一个ArrayList, doReadMessages(readBuf)方法就是将读到的链接放在这个list中, 因为这里是NioServerSocketChannel所以这走到了NioServerSocketChannel的doReadMessage()方法
跟到doReadMessage()方法中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 1protected int doReadMessages(List<Object> buf) throws Exception {
2 //根据当前jdk底层的serverSocketChannel拿到jdk底层channel
3 SocketChannel ch = javaChannel().accept();
4 try {
5 if (ch != null) {
6 //封装成一个NioSokectChannel扔到buf中
7 buf.add(new NioSocketChannel(this, ch));
8 return 1;
9 }
10 } catch (Throwable t) {
11 //代码省略
12 }
13 return 0;
14}
15
这里终于走到到了jdk底层相关的内容了
首先根据jdk的ServerSocketChannel拿到jdk的Channel, 熟悉Nio的小伙伴应该不会陌生
封装成一个NioSokectChannel扔到Readbuf中
这里的NioSocketChannel是对jdk底层的SocketChannel的包装, 我们看到其构造方法传入两个参数, this代表当前NioServerSocketChannel, ch代表jdk的SocketChannel
我们跟到NioSocketChannel的其造方法中:
1
2
3
4
5 1public NioSocketChannel(Channel parent, SocketChannel socket) {
2 super(parent, socket);
3 config = new NioSocketChannelConfig(this, socket.socket());
4}
5
这里看到调用了父类构造方法, 传入两个参数, parent代表创建自身channel的, NioServerSocketChannel, socket代表jdk底层的socketChannel
跟到父类构造方法中:
1
2
3
4 1protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
2 super(parent, ch, SelectionKey.OP_READ);
3}
4
其中SelectionKey.OP_READ代表其监听事件是读事件
继续跟父类的构造方法:
1
2
3
4
5
6
7
8
9
10
11
12 1protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
2 super(parent);
3 this.ch = ch;
4 this.readInterestOp = readInterestOp;
5 try {
6 //设置为非阻塞
7 ch.configureBlocking(false);
8 } catch (IOException e) {
9 //代码省略
10 }
11}
12
这里初始化了自身成员变量ch, 就是jdk底层的SocketChannel, 并初始化了自身的监听事件readInterestOp, 也就是读事件
ch.configureBlocking(false)这一步熟悉nio的小伙伴也不陌生, 就是将jdk的SocketChannel设置为非阻塞
我们继续跟到父类构造方法中:
1
2
3
4
5
6
7 1protected AbstractChannel(Channel parent) {
2 this.parent = parent;
3 id = newId();
4 unsafe = newUnsafe();
5 pipeline = newChannelPipeline();
6}
7
这里初始化parent, 也就是创建自身的NioServerSocketChannel, 并为自身创建了唯一id
初始化unsafe, 我们跟到newUnsafe()方法中
由于此方法是NioEventLoop调用的, 所以会走到其父类AbstractNioByteChannel的newUnsafe()
跟到newUnsafe()中:
1
2
3
4 1protected AbstractNioUnsafe newUnsafe() {
2 return new NioByteUnsafe();
3}
4
这里创建了NioByteUnsafe对象, 所以NioSocketChannel对应的unsafe是NioByteUnsafe
继续往下跟, 我们看到其初始化了pipeline, 有关pipline的知识, 我们会在下一章节中讲到
回到NioSocketChannel中的构造方法:
1
2
3
4
5 1public NioSocketChannel(Channel parent, SocketChannel socket) {
2 super(parent, socket);
3 config = new NioSocketChannelConfig(this, socket.socket());
4}
5
同NioServerSocketChannel一样, 这里也初始化了一个Config属性, 传入两个参数, 当前NioSocketChannel自身和jdk的底层SocketChannel的socket对象
我们跟进其构造方法:
1
2
3
4 1private NioSocketChannelConfig(NioSocketChannel channel, Socket javaSocket) {
2 super(channel, javaSocket);
3}
4
同样, 这个类是NioSocketChannel的内部类
继续跟父类构造方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 1public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) {
2 super(channel);
3 if (javaSocket == null) {
4 throw new NullPointerException("javaSocket");
5 }
6 //保存当前javaSocket
7 this.javaSocket = javaSocket;
8 //是否禁止Nagle算法
9 if (PlatformDependent.canEnableTcpNoDelayByDefault()) {
10 try {
11 setTcpNoDelay(true);
12 } catch (Exception e) {
13
14 }
15 }
16}
17
这里保存了SocketChannel的socket对象, 并且默认的情况禁止了Nagle算法, 有关Nagle, 感兴趣的同学可以学习下相关知识
继续跟到父类构造方法中:
1
2
3
4 1public DefaultChannelConfig(Channel channel) {
2 this(channel, new AdaptiveRecvByteBufAllocator());
3}
4
又跟到到了我们熟悉的部分了, 也就是说, 无论NioServerSocketChannel和NioSocketChannel, 最后都会初始化DefaultChannelConfig, 并创建可变ByteBuf分配器, 我们之前小节对此做过详细剖析这里不再赘述, 这部分忘记的内容可以阅读之前小节内容进行回顾
这个分配器什么时候真正分配字节缓冲的呢?我们会在之后的章节进行详细剖析
至此我们剖析完成了NioSocketChannel的初始化过程
上一节: 处理接入事件之handle的创建
下一节: NioSocketChannel注册到selector