FileChannel
FileChannel用于连接文件来读取和写入数据,它总是以阻塞模式运行,不支持调用configureBlocking方法设置为非阻塞类型。FileChannel不能够直接创建对象使用,它需要通过InputStream、outputStream或者RandomAccessFile来进行获取。例如:
@Test
public void test() throws FileNotFoundException {
FileInputStream inputStream = new FileInputStream("test.txt");
FileChannel fileChannel = inputStream.getChannel();
System.out.println(fileChannel);
RandomAccessFile accessFile = new RandomAccessFile("test.txt", "rw");
FileChannel channel = accessFile.getChannel();
System.out.println(channel);
FileOutputStream outputStream = new FileOutputStream("out.txt");
FileChannel channel1 = outputStream.getChannel();
System.out.println(channel1);
}
Channel通常需要配合Buffer一起使用,可以将Channel中的数据读取到Buffer中,也可以将Buffer中的数据写入到Channel中。使用Buffer前需要先为Buffer分配一定的内存空间,然后调用Channel的read方法将数据读取到Buffer中。当Channel使用完毕后,调用close方法主动的关闭Channel。
public static void main(String[] args) throws IOException {
RandomAccessFile file = new RandomAccessFile("test.txt", "rw");
FileChannel channel = file.getChannel(); // 获取Channel
ByteBuffer buffer = ByteBuffer.allocate(48); // 分配Buffer空间
int bytesRead = channel.read(buffer); // 读取数据
while(bytesRead != -1){
buffer.flip();
while(buffer.hasRemaining()){
System.out.println((char) buffer.get());
}
buffer.clear();
}
file.close();
channel.close();
}
如果想要往Channel中写入数据,需要调用Channel的write方法,例如:
@Test
public void channelWrite() throws IOException {
ByteBuffer byteBuffer = ByteBuffer.allocate(24);
for (int i = 0; i < 10; i++) {
byteBuffer.put((byte) i);
}
FileOutputStream outputStream = new FileOutputStream("out.txt");
FileChannel channel = outputStream.getChannel();
channel.write(byteBuffer);
System.out.println(channel.position());
channel.close();
outputStream.close();
}
除了read和write方法外,FileChannel还提供了一些其他的方法:
- size:获取和Channel连接的文件的大小
- truncate:根据给定的长度截断文件
- force:强制清除Channel未写入到磁盘中的剩余数据
- …
SocketChannel
SocketChannel用于连接TCP Socket,NIO提供了两种方法来创建SocketChannel:
- 自行打开一个SocketChannel来连接到网络上的服务器
- 当ServerSocketChannel获取到一个连接后可以创建一个SocketChannel
当SocketChannel使用完毕后,需要自行调用close方法关闭。
例如:
@Test
public void SocketChannelOpen() throws IOException {
SocketChannel channel = SocketChannel.open();
channel.connect(new InetSocketAddress("http://baidu.com", 80));
// 其他操作
channel.close();
}
同样也可以将SocketChannel中的数据读取到Buffer中,做法和FileChannel类似:
@Test
public void SocketChannelOpen() throws IOException {
SocketChannel channel = SocketChannel.open();
channel.connect(new InetSocketAddress("https://baidu.com", 80));
ByteBuffer byteBuffer = ByteBuffer.allocate(48);
int read = channel.read(byteBuffer);
System.out.println(read);
channel.close();
}
如果想要往SocketChannel中写入数据,同样调用write方法实现。
不同于FileChannel一个地方在于,SocketChannel支持调用configureBlocking(false)方法来实现非阻塞模式,从而实现异步的调用connect、read和write方法。
对于connect方法来说,如果它是以异步模式运行,那么调用connect不必等到连接建立后才返回。如果想要知道连接是否已经建立,可以调用finishConnect方法判断。例如:
cketChannel.configureBlocking(false); // 以非阻塞模式运行
socketChannel.connect(new InetSocketAddress("http://baidu.com", 80));
while(! socketChannel.finishConnect() ){ // 不断自旋,检查连接是否建立
// ...
}
对于write方法来说,如果它是以非阻塞模式运行,那么不必真正的写入数据才返回,因此,程序需要使用循环的方式来调用write方法。
对于read方法来说,如果它是以非阻塞模式运行,那么不必真正的读取到数据才返回。如果程序想判断是否真的读到了数据,可以从方法的int型返回值获知。
如果数据传输结束,调用close方法来关闭SocketChannel。
ServerSocketChannel
ServerSocketChannel用于监听可能到来的TCP连接,例如;
ServerSocketChannel channel = ServerSocketChannel.open();
channel.socket().bind(new InetSocketAddress(9999));
while(true){
SocketChannel socketChannel = channel.accept();
// ....
}
如果想要创建一个ServerSocketChannel,可以调用它的open方法实现,如上所示。当然,ServerSocketChannel使用完毕之后,也需要调用close方法自行关闭。
通过调用accept方法来监听到来的TCP连接,当连接到达时会返回一个SocketChannel。因此,如果监听开始后没有连接到达,那么accept方法会一直处于阻塞状态,如上所示。
当然,ServerSocketChannel也可以以非阻塞模式运行,此时accept方法调用后会立即返回。如果此时有连接到达,那么返回的SocketChannel不为null,否则为null。
ServerSocketChannel channel = ServerSocketChannel.open();
channel.socket().bind(new InetSocketAddress(9999));
channel.configureBlocking(false);
while(true){
SocketChannel socketChannel = channel.accept();
if(socketChannel != null){
// ...
}
// ....
}
DataGramChannel
DataGramChannel可以用来发送和接收UDP数据报,由于UDP是无连接的,因此程序无法像其他类型的Channel来读取和写入数据。类似的,通过调用open方法来获取DataGramChannel,通过receive方法可以获取数据报,如下所示:
DataGramChannel channel = DataGramChannel.open();
channel.socket().bind(new InetSocketAddress(9999));
ByteBuffer bufferr = ByteBuffer.allocate(24);
buffer.clear();
channel.receive(buffer);
receive方法获取到的数据报将被写入到Buffer中,如果数据量超过了Buffer的容量,那么多余的数据报将被直接丢弃。如果想要发送UDP数据报,可以调用send方法实现,如下所示:
DataGramChannel channel = DataGramChannel.open();
ByteBuffer buffer = ByteBuffer.allocate(48);
buf.put(....);
buffer.flip();
int bytesSent = channel.send(buffer, new InetSocketAddress("http://www.baidu.com", 80));
通过调用connect方法可以和一个特定的地址建立”连接”,它不是类似于TCP一样真正的连接,只是支持Channel只能向这个地址发送数据报或者接收数据报。
channel.connect(new InetSocketAddress("http://www.baidu.com", 80));
连接建立后,可以调用read和write方法,但是它无法保证数据的成功交付。
int bytesRad = channel.read(buffer);
int bytesWritten = channel.write(buf);
使用完毕后,调用close方法关闭Channel。