前言
在有了前几章的源码研究基础上,我们本章开始服务器的源码实现探索,本来打算将五种服务模型的源码都分析一遍,但看完源码后,感觉阻塞型服务实现还是比较简单,大家可以自己看看,这里我们只研究非阻塞型服务器源码,而所有的非阻塞型服务器都继承自AbstractNonblockingServer类,因此理解该类是理解非阻塞型服务实现的关键。
TServer
由于所有的服务模型都是由父类定义好框架,子类进行不同的实现,所以我们需要先了解AbstractNonblockingServer的父类TServer,TServer源码如下:
public abstract class TServer {
protected TProcessorFactory processorFactory_;//执行器工厂,在创建服务器的时候,我们需要一个TProcessorFactory的参数,即会赋给该变量,在收到请求时,通过该变量来执行业务逻辑
protected TServerTransport serverTransport_;//监听端口,接收请求
protected TTransportFactory inputTransportFactory_;//对transport进行封装获取transport,接收客户端数据时使用
protected TTransportFactory outputTransportFactory_;//对transport进行封装获取transport,输出服务端处理结果时使用
protected TProtocolFactory inputProtocolFactory_;//将transport封装为protocol对象,接收客户端数据时使用
protected TProtocolFactory outputProtocolFactory_;//将transport封装为protocol对象,输出服务端处理结果时使用
private boolean isServing;//服务是否运行中
protected TServerEventHandler eventHandler_;//服务器执行业务逻辑前的事件执行器(暂时没用,可忽略)
protected volatile boolean stopped_ = false;//服务是否已经停止
protected TServer(AbstractServerArgs args) {
processorFactory_ = args.processorFactory;
serverTransport_ = args.serverTransport;
inputTransportFactory_ = args.inputTransportFactory;
outputTransportFactory_ = args.outputTransportFactory;
inputProtocolFactory_ = args.inputProtocolFactory;
outputProtocolFactory_ = args.outputProtocolFactory;
}
public abstract void serve();//启动服务,由子类进行实现
public void stop() {}//停止服务
省略所有set get方法
}
//TTransportFactory源码
public class TTransportFactory {
public TTransport getTransport(TTransport trans) {
return trans;
}
}
源码中列举了所有成员变量和关键方法,如果对Thrift框架从客户端到服务端的请求流程有了解的同学,就会很容易理解各个变量在访问过程中的作用,这里给一幅图帮助大家理解。
图片来源于CSDN
至次,TServer的源码介绍完毕,TServer中相关Args的静态类这里不多介绍,大多的成员变量与TServer对应,起到传递的作用,没有什么业务逻辑,有兴趣的同学自己看下源码,下一节开始AbstractNonblockingServer源码探索。
AbstractNonblockingServer
概要
由于AbstractNonblockingServer内部的类多,且逻辑复杂,所以在后面研究源码之前,先向大家介绍一下,该类中有哪些内部类、静态类以及相应作用与联系,有助于后面理解源码。
图中我们只介绍类的作用,具体的AbstractNonblockingServer的方法作用我们在后面的章节详讲。
1.AbstractNonblockingServer:定义了非阻塞型服务的框架及运作流程。
2.AbstractNonblockingServerArgs:静态类,为非阻塞型服务提供的Args抽象类。
3.AbstractSelectThread:内部类,定义了一个抽象的非阻塞型IO操作的线程,具体是使用Java Nio中的Select。
4.FrameBufferState:私有类,表示FrameBuffer处于何种状态。
5.FrameBuffer:作为一个内部类,依赖AbstractNonblockingServer中的TransportFactory、ProtocolFactory来进行数据的读取、写入、业务接口调用操作。FrameBuffer会被绑定到一个SelectorKey对象,随着AbstractSelectThread轮循SelectorKey得到读写的执行机会,同时处于不同的状态下会在AbstractSelectThread中注册不同的interests事件,将当前通道在读写事件间相互转换。
6.AsyncFrameBuffer:内部类,FrameBuffer的异步实现,这里不讨论。
AbstractNonblockingServerArgs
我们先从简单的Args类开始,源码如下:
public static abstract class AbstractNonblockingServerArgs<T extends AbstractNonblockingServerArgs<T>> extends AbstractServerArgs<T> {
public long maxReadBufferBytes = Long.MAX_VALUE;
public AbstractNonblockingServerArgs(TNonblockingServerTransport transport) {
super(transport);
//设置父类inputTransportFactory_、outputTransportFactory_对象
transportFactory(new TFramedTransport.Factory());
}
}
相比其父类多了maxReadBufferBytes属性,用于设置读缓冲区的最大字节数,同时发现构造方法中传入了一个TFramedTransport的Factory,源码如下:
public static class Factory extends TTransportFactory {
private int maxLength_;
public Factory() {
maxLength_ = TFramedTransport.DEFAULT_MAX_LENGTH;
}
public Factory(int maxLength) {
maxLength_ = maxLength;
}
@Override
public TTransport getTransport(TTransport base) {
return new TFramedTransport(base, maxLength_);
}
}
从传入的Factory源码可知,从inputTransportFactory_、outputTransportFactory_ 获取的Transport对象是一个封装好的TFramedTransport对象,也解答了为何当服务端是非阻塞模式时,客户端必须使用TFramedTransport的原因,因为非阻塞服务端会将Transport对象封装为TFramedTransport,关于TFramedTransport,大家只需要知道,TFramedTransport保证先只读四个字段,因为前四个字节代表这次请求或回应的数据量大小,从而让服务端、客户端知道该接收多少字节数的数据代表接收完毕(Java Nio中使用ByteBuffer来保存数据,需要预先设定好ByteBuffer的大小才能保证请求数据读取完整,所以用前四节代表数据字节数大小,预先设定ByteBuffer的size)。
AbstractNonblockingServer
AbstractNonblockingServer源码及备注如下:
public abstract class AbstractNonblockingServer extends TServer {
protected final Logger LOGGER = LoggerFactory.getLogger(getClass().getName());
final long MAX_READ_BUFFER_BYTES;//对应AbstractNonblockingServerArgs中的maxReadBufferBytes
final AtomicLong readBufferBytesAllocated = new AtomicLong(0);//已分配的读缓存的字节数
public AbstractNonblockingServer(AbstractNonblockingServerArgs args) {
super(args);
MAX_READ_BUFFER_BYTES = args.maxReadBufferBytes;
}
//启动服务
public void serve() {
//启动IO线程
if (!startThreads()) {
return;
}
//开始监听端口,接收客户端请求
if (!startListening()) {
return;
}
//修改服务状态为正在服务中
setServing(true);
//启动服务后的阻塞方法,服务停止后通过
waitForShutdown();
//修改服务状态为结束服务
setServing(false);
//停止监听端口
stopListening();
}
//启动IO线程,子类实现
protected abstract boolean startThreads();
//启动服务后的阻塞方法,服务停止后通过,子类实现
protected abstract void waitForShutdown();
//开始监听端口,原理和ServerSocket一样,不再详述listen()方法
protected boolean startListening() {
try {
serverTransport_.listen();
return true;
} catch (TTransportException ttx) {
LOGGER.error("Failed to start listening on server socket!", ttx);
return false;
}
}
//停止监听,同上
protected void stopListening() {
serverTransport_.close();
}
//对frameBuffer执行业务逻辑,子类实现,后面讲解
protected abstract boolean requestInvoke(FrameBuffer frameBuffer);
}
上述源码确定了所有非阻塞类型服务的服务框架,而startThreads()、waitForShutdown()、requestInvoke(FrameBuffer frameBuffer)的实现便显得十分重要,它们会决定服务的效率,整个流程还是容易理解的,下面来看里面用到的其他类。
FrameBuffer
FrameBuffer是非阻塞服务中真正通过调用Protocol接口实现数据的读写操作的类,与selectionKey进行绑定,会在Selector线程轮循时,让FrameBuffer对象获取到读写的机会。同时是AbstractNonblockingServer的内部类(非静态),由于该类存在多种状态,并根据不同状态表现不同的行为,这里我们介绍下它的所有状态:
private enum FrameBufferState {
//处于读取 FrameSize的状态,即正在读取本次数据大小值的,读完后进入下一状态
READING_FRAME_SIZE,
//正在读真正数据,读完真正数据进入下一状态
READING_FRAME,
//数据读取完毕,可调用业务处理方法,业务调用结束后进入下一状态
READ_FRAME_COMPLETE,
//业务调用结束,等待被注册为写事件,注册完后进入下一状态
AWAITING_REGISTER_WRITE,
//开始向客户端输出结果,输出完后进入下一状态
WRITING,
//结果处理完毕,再次等待注册为可读事件,注册成功后回到READING_FRAME_SIZE状态
AWAITING_REGISTER_READ,
//前面几个状态出现问题,或者其他情况下,需要关闭该连接时处于该状态,selector轮循时关闭该连接
AWAITING_CLOSE
}
这里我画了一幅图方便理解:
下面先贴上FrameBuffer的源码。
public class FrameBuffer {
private final Logger LOGGER = LoggerFactory.getLogger(getClass().getName());
protected final TNonblockingTransport trans_;//与客户端创建的连接,具体的实现是TNonblockingSocket
protected final SelectionKey selectionKey_;// 当前对象绑定的SelectionKey对象,通过selector轮循会访问到当前对象
protected final AbstractSelectThread selectThread_;//当前对象所属的 selectThread 线程(用于读写IO操作的线程)
protected FrameBufferState state_ = FrameBufferState.READING_FRAME_SIZE;//当前对象所处的状态,上面已介绍过
protected ByteBuffer buffer_;//Java Nio中的buffer对象,trans_进行读写时使用
protected final TByteArrayOutputStream response_;//执行完业务逻辑后,保存在本地的结果,responseReady()中会将这里面的数据复制到buffer_
protected final TMemoryInputTransport frameTrans_;//invoke()方法执行时,会将buffer_中的数据复制到此变量
//下面的四个变量的意义顾名思议,不再多讲,具体的依赖关系查看下面构造方法
protected final TTransport inTrans_;
protected final TTransport outTrans_;
protected final TProtocol inProt_;
protected final TProtocol outProt_;
protected final ServerContext context_;//暂不关心,可理解为在业务逻辑调用前,可能需要调用的执行
public FrameBuffer(final TNonblockingTransport trans,
final SelectionKey selectionKey,
final AbstractSelectThread selectThread) {
trans_ = trans;
selectionKey_ = selectionKey;
selectThread_ = selectThread;
buffer_ = ByteBuffer.allocate(4);//Java Nio相关,第一次读数据的大小FrameSize,所以分配4字节,与TFramedTransport源码对应
frameTrans_ = new TMemoryInputTransport();//客户端的请求数据 会缓存在该变量中
response_ = new TByteArrayOutputStream();//不用了解该类,知道保存返回结果即可
inTrans_ = inputTransportFactory_.getTransport(frameTrans_);//这里实际调用时会返回一个TFramedTransport对象,原因在上面AbstractNonblockingServerArgs源码讲解中,FrameBuffer是AbstractNonblockingServer的内部类,所以能访问外部对象,而AbstractNonblockingServer的inputTransportFactory_、outputProtocolFactory_对象是由AbstractNonblockingServerArgs的参数确定
outTrans_ = outputTransportFactory_.getTransport(new TIOStreamTransport(response_));//同上
inProt_ = inputProtocolFactory_.getProtocol(inTrans_);//将transport封装为protocol
outProt_ = outputProtocolFactory_.getProtocol(outTrans_);//将transport封装为protocol
//所以其实 inProt_和outProt_最终的引用是frameTrans_、response_。
if (eventHandler_ != null) {
context_ = eventHandler_.createContext(inProt_, outProt_);
} else {
context_ = null;
}
}
//给当前FrameBuffer一个机会去读,不管读多读少,只要正确读到数据就返回true(即使没读完下次还会继续),读取失败或者参数异常等返回false,返回false即当前FrameBuffer会被清除掉
1.如果处于READING_FRAME_SIZE状态,则一直到FrameSize读取成功,并通过校验,通过校验后分配buffer,并修改状态到READING_FRAME状态,下次调用该方法时进入方法2
2.如果处于READING_FRAME状态,则一直到整个Frame读完,注销掉当前的读事件,并修改READ_FRAME_COMPLETE状态
public boolean read() {
//处于读 FrameSize状态,需要先读到数据量的大小
if (state_ == FrameBufferState.READING_FRAME_SIZE) {
// 该方法下面贴出,作用是从trans_往buffer_中读取一次数据,读
if (!internalRead()) {
return false;
}
//==0代表buffer_已经读完了FrameSize
if (buffer_.remaining() == 0) {
int frameSize = buffer_.getInt(0);
//下面三个if是对frameSize合法性的判断
if (frameSize <= 0) {
LOGGER.error("Read an invalid frame size of " + frameSize
+ ". Are you using TFramedTransport on the client side?");
return false;
}
if (frameSize > MAX_READ_BUFFER_BYTES) {//还记得在AbstractNonblockingServer中的MAX_READ_BUFFER_BYTES么
LOGGER.error("Read a frame size of " + frameSize
+ ", which is bigger than the maximum allowable buffer size for ALL connections.");
return false;
}
//如果已经超出可分配的字节数,返回true,等待下次是否有释放掉的字节。
//还记得AbstractNonblockingServer中的readBufferBytesAllocated么
if (readBufferBytesAllocated.get() + frameSize > MAX_READ_BUFFER_BYTES) {
return true;
}
//通过校验后,已分配字节数自增
readBufferBytesAllocated.addAndGet(frameSize + 4);
//分配空间,并将数据大小放进去
buffer_ = ByteBuffer.allocate(frameSize + 4);
buffer_.putInt(frameSize);
//修改状态到 正在读取数据中
state_ = FrameBufferState.READING_FRAME;
} else {
//还没有将FrameSize读完时,返回true,下次继续读,直到读完才走到上面的逻辑
return true;
}
}
//FrameSize读完,处于正在读数据中
if (state_ == FrameBufferState.READING_FRAME) {
if (!internalRead()) {//读一次
return false;
}
//正体数据,Frame也读完了
if (buffer_.remaining() == 0) {
selectionKey_.interestOps(0);//注销掉当前BufferFrame的read事件
state_ = FrameBufferState.READ_FRAME_COMPLETE;//修改为读完数据可执行状态
}
return true;
}
//走到这一步说明当前state是不对的,返回错误
LOGGER.error("Read was called but state is invalid (" + state_ + ")");
return false;
}
//尝试写一次
public boolean write() {
//处于正在写的状态
if (state_ == FrameBufferState.WRITING) {
try {
//尝试将buffer_中的结果返回给客户端
if (trans_.write(buffer_) < 0) {
return false;
}
} catch (IOException e) {
LOGGER.warn("Got an IOException during write!", e);
return false;
}
//==0表示已经写完,
if (buffer_.remaining() == 0) {
prepareRead();//准备切换回读模式
}
return true;
}
//到达这一步表示状态异常,直接返回false
LOGGER.error("Write was called, but state is invalid (" + state_ + ")");
return false;
}
//修改当前FrameBuffer绑定的selectionKey_在selector中的事件注册类型,只有涉及到 selector事件时才调用该方法,基本为FrameBuffer读完、写完、异常情况
public void changeSelectInterests() {
if (state_ == FrameBufferState.AWAITING_REGISTER_WRITE) {
//当FrameBuffer处于AWAITING_REGISTER_WRITE状态时,注册写事件到selector中,并将当前状态改为WRITING状态
selectionKey_.interestOps(SelectionKey.OP_WRITE);
state_ = FrameBufferState.WRITING;
} else if (state_ == FrameBufferState.AWAITING_REGISTER_READ) {
//如果是AWAITING_REGISTER_READ状态,则调用prepareRead()方法,准备下一次读取
prepareRead();
} else if (state_ == FrameBufferState.AWAITING_CLOSE) {
//如果处于AWAITING_CLOSE状态,说明当前FrameBuffer有异常,可能是网络、或者客户端断开连接等等造成,调用close方法关闭当前FrameBuffer,并取消selectionKey_
close();
selectionKey_.cancel();
} else {
//状态异常,打印日志
LOGGER.error("changeSelectInterest was called, but state is invalid (" + state_ + ")");
}
}
//关闭当前FrameBuffer
public void close() {
// if we're being closed due to an error, we might have allocated a
// buffer that we need to subtract for our memory accounting.
if (state_ == FrameBufferState.READING_FRAME ||
state_ == FrameBufferState.READ_FRAME_COMPLETE ||
state_ == FrameBufferState.AWAITING_CLOSE) {
readBufferBytesAllocated.addAndGet(-buffer_.array().length);
}
trans_.close();
//不用关心
if (eventHandler_ != null) {
eventHandler_.deleteContext(context_, inProt_, outProt_);
}
}
//查看Frame是否读完
public boolean isFrameFullyRead() {
return state_ == FrameBufferState.READ_FRAME_COMPLETE;
}
//准备返回结果,在业务逻辑处理完后调用
public void responseReady() {
//读缓存已经使用完毕,进行释放
readBufferBytesAllocated.addAndGet(-buffer_.array().length);
if (response_.len() == 0) {
//不需要返回,则直接改为AWAITING_REGISTER_READ状态,准备下次读取
state_ = FrameBufferState.AWAITING_REGISTER_READ;
buffer_ = null;
} else {
//将结果放入到buffer_中
buffer_ = ByteBuffer.wrap(response_.get(), 0, response_.len());
//进入等待注册写 模式
state_ = FrameBufferState.AWAITING_REGISTER_WRITE;
}
//注册selector事件变化
requestSelectInterestChange();
}
//FrameBuffer 执行业务 逻辑的地方
public void invoke() {
frameTrans_.reset(buffer_.array());//将buffer_的请求数据复制到frameTrans_本地内存中
response_.reset();//置空response_
try {
if (eventHandler_ != null) {//忽略
eventHandler_.processContext(context_, inTrans_, outTrans_);
}
//执行业务逻辑
processorFactory_.getProcessor(inTrans_).process(inProt_, outProt_);
responseReady();//准备返回结果
return;
} catch (TException te) {
LOGGER.warn("Exception while invoking!", te);
} catch (Throwable t) {
LOGGER.error("Unexpected throwable while invoking!", t);
}
//到达这里说明执行有异常,进入等待关闭状态,并注册事件
state_ = FrameBufferState.AWAITING_CLOSE;
requestSelectInterestChange();
}
//从trans_往缓存buffer_中读取一次数据
private boolean internalRead() {
try {
if (trans_.read(buffer_) < 0) {
return false;
}
return true;
} catch (IOException e) {
LOGGER.warn("Got an IOException in internalRead!", e);
return false;
}
}
//准备读方法,
private void prepareRead() {
//在selector注册读事件
selectionKey_.interestOps(SelectionKey.OP_READ);
//为下一次读取进行变量初始化,同构造方法对应
buffer_ = ByteBuffer.allocate(4);
state_ = FrameBufferState.READING_FRAME_SIZE; //回到最初状态
}
//请求修改注册事件类型,如果是所属的selectThread执行,直接调用方法即可,非当前线程执行需要将当前FrameBuffer的interests事件注册到当前所属SelectThread中
protected void requestSelectInterestChange() {
if (Thread.currentThread() == this.selectThread_) {
changeSelectInterests();
} else {
this.selectThread_.requestSelectInterestChange(this);
}
}
}
由于源码篇幅过长,这里总结下FrameBuffer主要方法的作用:
1.read():给FrameBuffer一次机会读数据,读多少不能保证。
2.write():给FrameBuffer一次机会写数据,写多少不能保证。
3.changeSelectInterests():修改当前FrameBuffer绑定的SelectorKey在AbstractSelectThread注册的事件类型,一般是读、写、关闭三种事件需要转换时调用。
4.close():关闭当前连接,释放相应资源。
5.isFrameFullyRead():当前FrameBuffer是否已将客户端的请求数据读取完成。
6.responseReady():业务执行完,在返回结果前做准备工作,将结果拷贝到buffer_中。
7.invoke():从buffer_拷贝数据到frameTrans_,然后执行业务逻辑,最后准备返回结果。
8.prepareRead():准备回到最初始的 读FrameSize状态。
9.requestSelectInterestChange():请求将当前FrameBuffer发生的interests事件变化执行或注册到AbstractSelectThread中。
补充:
AbstractSelectThread
顾名思义,用于Selector非阻塞IO读写的线程,直接贴源码。
protected abstract class AbstractSelectThread extends Thread {
protected final Selector selector;//NIO的selector,不多讲
protected final Set<FrameBuffer> selectInterestChanges = new HashSet<FrameBuffer>();//FrameBuffer需要修改已注册到selector的事件时,将把自己放到这个集合,例如业务逻辑处理完后,从读事件到写事件的转换,即会在这里注册
public AbstractSelectThread() throws IOException {
this.selector = SelectorProvider.provider().openSelector();
}
//唤醒selector
public void wakeupSelector() {
selector.wakeup();
}
//将FrameBuffer添加到selector interest变化集合中,如果selector阻塞,则唤醒
public void requestSelectInterestChange(FrameBuffer frameBuffer) {
synchronized (selectInterestChanges) {
selectInterestChanges.add(frameBuffer);
}
selector.wakeup();
}
//检查是否有FrameBuffer需要修改他们的interest
protected void processInterestChanges() {
synchronized (selectInterestChanges) {
for (FrameBuffer fb : selectInterestChanges) {
fb.changeSelectInterests();
}
selectInterestChanges.clear();//清空事件集合
}
}
//从客户端读取数据,如果已经读完,则执行业务处理
protected void handleRead(SelectionKey key) {
FrameBuffer buffer = (FrameBuffer) key.attachment();
if (!buffer.read()) {
cleanupSelectionKey(key);//读取失败清除当前连接
return;
}
//如果请求数据读取完毕,执行业务逻辑,执行失败取消连接
if (buffer.isFrameFullyRead()) {
if (!requestInvoke(buffer)) {
cleanupSelectionKey(key);
}
}
}
//如果有返回结果,向客户端返回数据
protected void handleWrite(SelectionKey key) {
FrameBuffer buffer = (FrameBuffer) key.attachment();
if (!buffer.write()) {
cleanupSelectionKey(key);
}
}
//连接关闭时的一些清理工作
protected void cleanupSelectionKey(SelectionKey key) {
FrameBuffer buffer = (FrameBuffer) key.attachment();
if (buffer != null) {
buffer.close();//关闭Socket
}
//注销key
key.cancel();
}
}
总结AbstractSelectThread的方法如下:
1.wakeupSelector():唤醒selector。
2.requestSelectInterestChange(FrameBuffer frameBuffer):添加frameBuffer到select事件集合。
3.processInterestChanges():执行select事件集合的中注册的事件。
4.handleRead(SelectionKey key):尝试对key进行一次读操作。
5.handleWrite(SelectionKey key):尝试对key进行一次写操作。
6.cleanupSelectionKey(SelectionKey key):关闭key连接并做相关清理工作。
调用流程
至次,AbstractNonblockingServer中所有源码已经讲完,但还是比较难理解的,下面贴一张图来梳理AbstractSelectThread、FrameBuffer、AbstractNonblockingServer间的关系,理解了这三者的关系对于理解非阻塞型的服务模型非常关键。
其中AbstractSelectThread中handleRead(SelectionKey key),processInterestChanges(),handleWrite(SelectionKey key)是子类调用的方法入口,我们按照 一次请求的流程来介绍整个过程。
1.1.子类调用handRead(SelectionKey key)方法时,会对传入的SelectionKey绑定的FrameBuffer调用read()方法,这里read()可能一次不会读完,有可能多次handRead方法调用才会读完数据,最终读完数据状态转为READ_FRAME_COMPLETE,从而isFrameFullyRead()才会通过。
1.2.读完数据后,会调用用子类的requestInvoke(buffer)方法,内部最终回调FrameBuffer.invoke()方法,进行业务逻辑处理。
1.3.业务调用结束后,调整FrameBuffer进入AWAITING_REGISTER_WRITE或AWAITING_REGISTER_READ状态,然后将变更Selector事件类型,这里的requestSelectInterestChange()方法会有判断当前线程是否为所属Select线程,是因为非阻塞服务模型中有单线程、多线程,一般来说,多线程由于业务逻辑的执行是线程池在调用,所以肯定是调用AbstractSelectThread.requestSelectInterestChange(FrameBuffer frameBuffer)将事件变更注册到AbstractSelectThread的事件集合中。
2.processInterestChanges()由子类调用后,会对事件集合中的FrameBuffer进行已注册的事件转换。
3.handleWrite(SelectionKey key)由子类调用后,会对传入的SelectionKey绑定的FrameBuffer调用write()方法,同read()一样,可能需要多次才能写完,写完后又回到READING_FRAME_SIZE状态。
注意:handleRead,handleWrite调用时,如果读写操作出错,则调用cleanupSelectionKey(SelectionKey key)清理key和释放FrameBuffer相关资源。
总结
这一章讲解了非阻塞型服务器的父类AbstractNonblockingServer中的源码,重点希望读者能理解AbstractSelectThread、FrameBuffer、AbstractNonblockingServer三者间的关系,TNonblockingServer、THsHaServer实现过于简单,后面不做介绍,下章我们直接探索TThreadedSelectorServer的源码实现。