在前两篇博客中,我们讲解了服务端的启动和EventLoop相关的源码,接下来,我们看一下服务端是如何监听客户端接入并建立连接的:
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch)
当客户端想和服务端建立连接的时候,会被服务端channel绑定的selector轮询到这个io事件,之后就会调用上面这个方法来处理这个io事件,接下来我们就在这个地方开始展开:
//获得服务端channel的unsafe类
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
//验证key的有效性
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
return;
}
if (eventLoop != this || eventLoop == null) {
return;
}
unsafe.close(unsafe.voidPromise());
return;
}
一开始需要获得unsafe类,毕竟那些io事件都是通过它来处理的,接着就要验证attachment的有效性:
int readyOps = k.readyOps();
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
if (!ch.isOpen()) {
// Connection already closed - no need to handle write.
return;
}
}
之后就会获得当前attachment注册的感兴趣的事件,对于服务端channel就是accept事件,所以最终会进入这个分支,之后就会调用unsafe类的read方法,在这不要把read方法混淆,由于服务端的channel是专门用来处理客户端连接的,所以它内部创建的unsafe类也是专门处理连接的,进去看一下read方法:
public void read() {
assert eventLoop().inEventLoop();
//获得当前channel的配置
final ChannelConfig config = config();
//获得当前channel的pipeline
final ChannelPipeline pipeline = pipeline();
//分配一个新的接收缓冲区,该缓冲区的容量可能大到足以读取所有入站数据,也足够小不要浪费空间。
final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();
allocHandle.reset(config);
boolean closed = false;
Throwable exception = null;
try {
try {
//在这里面是来循环读取请求的连接,先存储到readBuf中
do {
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (loca