四注册与处理过程详解
接下来我们要分析Connection的register()方法前面我们总是说用Selector注册的连接其实这是一种简化的说法实际上用Selector注册的是一个javaniochannelsSocketChannel对象但只针对特定的I/O操作注册之后有一个javaniochannelsSelectionKey被返回这个选择键可以通过attach()方法关联到任意对象为了通过键获得连接这里把Connection对象关联到键这样我们就可以从Selector间接地获得一个Connection
public void register(Selector selector)
throws IOException {
key = socketChannelregister(selector SelectionKeyOP_READ);
keyattach(this);
}
回过头来看ConnectionSelectorselect()方法的返回值表示有多少连接已经做好了I/O操作的准备如果返回值是则返回否则调用selectedKeys()获得键的集合(Set)从这些键获得以前关联的Connection对象然后调用其readRequest()或writeResponse()方法具体调用哪一个方法由连接被注册为读取操作还是写入操作决定
现在再来看Connection类Connection类代表着连接处理所有协议有关的细节在构造函数中通过参数传入的SocketChannel被设置成非阻塞模式这对于服务器来说是很重要的另外构造函数还设置了一些默认值分配了缓沖区requestLineBuffer由于分配直接缓沖区代价稍高且这里的每一个连接都用一个新的缓沖区因此这里使用javanioByteBufferallocate()而不是ByteBufferallocateDirect()如果重用缓沖区直接缓沖区可能具有更高的效率
public Connection(SocketChannel socketChannel)
throws IOException {
thissocketChannel = socketChannel;
nfigureBlocking(false);
requestLineBuffer = ByteBufferallocate();
}
完成所有初始化工作且SocketChannel做好了读取准备之后ConnectionSelector调用了readRequest()方法利用socketChannelread(requestLineBuffer)方法把所有可用的数据读入缓沖区如果不能读取完整的行则返回发出调用的ConnectionSelector允许另一个连接进入处理过程反之如果成功地读取了整个行接下来应该做的是象在Httpd中一样解析请求如果当前的请求合法程序为请求目标文件创建一个javanioChannelsFileChannel并调用prepareForResponse()方法
private void prepareForResponse() throws IOException {
StringBuffer responseLine = new StringBuffer();
responseLineBuffer = ByteBufferwrap(
responseLinetoString()getBytes(ASCII)
);
keyinterestOps(SelectionKeyOP_WRITE);
keyselector()wakeup();
}