Java NIO的主要构成核心就是Buffer、Channel和Selector这三个。本篇文章讲述Channel;
通常来说, 所有的 NIO 的 I/O 操作都是从 Channel 开始的. 一个 channel 类似于一个 stream(InputStream/OutputStream);
java Stream 和 NIO Channel 对比
我们可以在同一个 Channel 中执行读和写操作,
然而同一个Stream 仅仅支持读或写.
Channel 可以异步地读写, 而 Stream 是阻塞的同步读写.
Channel 总是从 Buffer 中读取数据, 或将数据写入到 Buffer 中.
通常来说NIO中的所有IO都是从 Channel(通道) 开始的。
从通道进行数据读取
创建一个缓冲区,然后请求通道读取数据。
从通道进行数据写入
创建一个缓冲区,填充数据,并要求通道写入数据。
数据读取和写入操作图示:
Channel 类型
FileChannel: 用于文件的数据读写
DatagramChannel: 用于UDP的数据读写
SocketChannel: 用于TCP的数据读写,一般是客户端实现
ServerSocketChannel: 允许我们监听TCP链接请求,每个请求会创建会一个SocketChannel,一般是服务器实现
Channel的UML类图
几种Channel的使用示例
基本的 Channel 使用例子:
public static void main( String[] args ) throws Exception{
RandomAccessFile aFile = new RandomAccessFile("/Users/xiongyongshun/settings.xml", "rw");
FileChannel inChannel = aFile.getChannel();
ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); while (bytesRead != -1) {
buf.flip(); while(buf.hasRemaining()){
System.out.print((char) buf.get());
}
buf.clear();
bytesRead = inChannel.read(buf);
}
aFile.close();
}
FileChannel 是操作文件的Channel, 我们可以通过 FileChannel 从一个文件中读取数据, 也可以将数据写入到文件中.注意
, FileChannel 不能设置为非阻塞模式.
RandomAccessFile aFile= new RandomAccessFile("test.txt", "rw");
FileChannel inChannel = aFile.getChannel();
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();while(buf.hasRemaining()) {
channel.write(buf);
}
当我们对 FileChannel 的操作完成后, 必须将其关闭
channel.close();
long pos channel.position();
channel.position(pos +123);
我们可以通过 channel.size()获取关联到这个 Channel 中的文件的大小.
注意:这里返回的是文件的大小, 而不是 Channel 中剩余的元素个数.
channel.truncate(1024);
将文件的大小截断为1024字节.
我们可以强制将缓存的未写入的数据写入到文件中:
channel.force(true);
SocketChannel 是一个客户端用来进行 TCP 连接的 Channel.
打开一个 SocketChannel, 然后将其连接到某个服务器中
当一个 ServerSocketChannel 接受到连接请求时,
会返回一个 SocketChannel 对象.
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(
new InetSocketAddress("http://example.com", 80));
socketChannel.close();
ByteBuffer buf = ByteBuffer.allocate(48);int bytesRead = socketChannel.read(buf);
如果 read()返回 -1, 那么表示连接中断了.
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();while(buf.hasRemaining()) {
channel.write(buf);
}
ServerSocketChannel 顾名思义, 是用在服务器为端的, 可以监听客户端的 TCP 连接, 例如:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
while(true){
SocketChannel socketChannel =
serverSocketChannel.accept();
//do something with socketChannel...
}
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.close();
我们可以使用ServerSocketChannel.accept()方法来监听客户端的 TCP 连接请求, accept()方法会阻塞, 直到有连接到来, 当有连接时, 这个方法会返回一个 SocketChannel 对象:
while(true){
SocketChannel socketChannel =
serverSocketChannel.accept();
//do something with socketChannel...
}
在非阻塞模式下, accept()是非阻塞的, 因此如果此时没有连接到来, 那么 accept()方法会返回null:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);while(true){ SocketChannel socketChannel =
serverSocketChannel.accept(); if(socketChannel != null){ //do something with socketChannel...
}
}
DatagramChannel 是用来处理 UDP 连接的.
DatagramChannel channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(9999));
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
channel.receive(buf);
String newData = "New String to write to file..."
+ System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();int bytesSent = channel.send(buf, new InetSocketAddress("example.com", 80));
因为 UDP 是非连接的, 因此这个的 connect 并不是向 TCP 一样真正意义上的连接, 而是它会讲 DatagramChannel 锁住, 因此我们仅仅可以从指定的地址中读取或写入数据.
channel.connect(new InetSocketAddress("example.com", 80));
参考:https://segmentfault.com/a/1190000006824107