Channel 通道
通道表示到实体(如硬件设备、文件、网络套接字或程序组件)的开放连接,该实体能够执行一个或多个不同的I/O操作,例如读取或写入
FileChannel
- 用于读取、写入、映射和操作文件的通道
- 文件通道是连接到文件的
SeekableByteChannel
。它在其文件中有一个当前的position,可以是查询position,也可以是修改的position(long)。文件本身包含一个可变长度的字节序列,可以读取和写入,并且可以查询当前的size。当写入的字节超过当前大小时,文件大小会增加;当文件truncated时,文件大小会减小。文件还可能具有一些关联的元数据,例如访问权限、内容类型和上次修改时间;此类不定义元数据访问的方法。
FileChannel的类继承关系:
- FileChannel的读写分离体现了接口的隔离原则
- FileChannel中的read函数根据直接或非直接ByteBuffer读取到堆缓冲区或者直接返回buffer缓冲区
SocketChannel
- 网络套接字Socket和上面文件是一样的,在Linux上都抽象成了文件描述符fd
- SocketChannel面向流的连接套接字的可选通道
- 通过调用此类的
open
方法创建套接字通道。无法为任意的、预先存在的套接字创建通道。新创建的套接字通道已打开,但尚未连接。试图在未连接的通道上调用I/O操作将导致引发NotYetConnectedException
。套接字通道可以通过调用其connect
方法进行连接;一旦连接,套接字通道将保持连接,直到关闭。套接字通道是否连接可以通过调用其isConnected
方法来确定 - SocketChannel支持无阻塞连接
SocketChannel的类继承关系:
Selector
Selector介绍
-
Selector
是SelectableChannel
对象的多路复用选择器 - 可以通过调用此类的
open
方法来创建选择器,该方法将使用系统默认的java.nio.channels.spi.SelectorProvider
选择器来创建新的选择器。还可以通过调用自定义选择器java.nio.channels.spi.SelectorProvider.openSelector
方法来创建选择器。选择器保持打开状态,直到通过其close
方法关闭为止 -
SelectableChannel
注册Selector
,并选择感兴趣的SelectionKey
。Selector
维护三组SelectionKey
:-
key set:包含表示此选择器当前
Channel
注册的所有key
。此集合由keys()
方法返回 -
selected-key set:是一组键,用来存放在一个selection期间,那些之前注册了感兴趣的
SelectionKey
的Channel
已准备好可以操作的key的集合。此集合由selectedKeys()
方法返回。selected-key set
始终是ket set
的子集 -
cancelled-key set:是已取消但其
Channel
尚未注销的键集。此集合不能直接访问。cancelled-key set
始终是ket set
的子集
-
key set:包含表示此选择器当前
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
使用SelectionKey的注意点
-
SelectionKey返回的集合在Handler响应的Key事件之后,需要将Key显式的删除,否在下一次
select
的时候,这个Key还在,而且删除需要用key的iterator
进行删除,否则将会抛出异常(Java SE基础知识,容器删除元素后容器大小变化造成)
使用Selector的Java NIO示例代码
public class Server {
public static void main(String[] args) throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress("127.0.0.1", 8888));
ssc.configureBlocking(false);
System.out.println("server started, listening on :" + ssc.getLocalAddress());
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
while(true) {
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while(it.hasNext()) {
SelectionKey key = it.next();
it.remove();
handle(key);
}
}
}
private static void handle(SelectionKey key) {
if(key.isAcceptable()) {
try {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
//new Client
//
//String hostIP = ((InetSocketAddress)sc.getRemoteAddress()).getHostString();
/*
log.info("client " + hostIP + " trying to connect");
for(int i=0; i<clients.size(); i++) {
String clientHostIP = clients.get(i).clientAddress.getHostString();
if(hostIP.equals(clientHostIP)) {
log.info("this client has already connected! is he alvie " + clients.get(i).live);
sc.close();
return;
}
}*/
sc.register(key.selector(), SelectionKey.OP_READ );
} catch (IOException e) {
e.printStackTrace();
} finally {
}
} else if (key.isReadable()) { //flip
SocketChannel sc = null;
try {
sc = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(512);
buffer.clear();
int len = sc.read(buffer);
if(len != -1) {
System.out.println(new String(buffer.array(), 0, len));
}
ByteBuffer bufferToWrite = ByteBuffer.wrap("HelloClient".getBytes());
sc.write(bufferToWrite);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(sc != null) {
try {
sc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。