FileChannel是一个连接到文件的通道,可以通过文件通道读写文件。它无法设置为非阻塞模式,总是运行在阻塞模式下。
我们可以通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例。例如:
RandomAccessFile aFile = new RandomAccessFile("D:/demo/data.txt", "rw");
FileChannel inChannel = aFile.getChannel();
调用FileChannel.read()方法,例如:
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);
read()方法返回的int值表示了有多少字节被读到了Buffer中。如果返回-1,表示到了文件末尾。
调用FileChannel.write()方法,例如:
String newData = "Some data";
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
channel.write(buf);
}
因为无法保证write()方法一次能向FileChannel写入多少字节,因此需要重复调用write()方法,直到Buffer中已经没有尚未写入通道的字节。
用完FileChannel后必须将其关闭,例如:
channel.close();
调用position()方法可以获取FileChannel的当前位置,调用position(long pos)方法设置FileChannel的当前位置。例如:
long pos = channel.position();
channel.position(pos + 100);
如果将位置设置在文件结束符之后,然后从通道中读数据,读方法将返回-1 ,也就是文件结束标志。
如果将位置设置在文件结束符之后,然后向通道中写数据,文件将撑大到当前位置并写入数据。这可能导致“文件空洞”,磁盘上物理文件中写入的数据间有空隙。
long fileSize = channel.size();
调用FileChannel.truncate()方法,截取文件时,文件中指定长度后面的部分将被删除。如:
channel.truncate(1024);
操作系统一般会将数据缓存在内存中,写入到FileChannel里的数据不一定会即时写到磁盘上。调用force()方法可以将通道里尚未写入磁盘的数据强制写到磁盘上。
force()方法有一个boolean类型的参数,true表示同时将文件元数据(权限信息等)写到磁盘上,例如:
channel.force(true);
SocketChannel是一个连接到TCP网络套接字的通道。
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://www.xxx.com", 80));
socketChannel.close();
同读FileChannel。
同写FileChannel。
非阻塞模式下调用connect(),该方法可能在连接建立之前就返回了。为了确定连接是否建立,可以调用finishConnect()方法。
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://www.xxx.com", 80));
while(!socketChannel.finishConnect() ){
// wait, or do something else
}
非阻塞模式下,read()方法在尚未读到任何数据时可能就返回了。所以需要关注它的int返回值,它会告诉你读取了多少字节。
非阻塞模式下,write()方法在尚未写出任何数据时可能就返回了。所以需要在循环中调用write()。
ServerSocketChannel 是一个可以监听新进来的TCP连接的通道,就像标准IO中的ServerSocket一样。
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.close();
用ServerSocketChannel.accept()方法监听新进来的连接。当 accept()方法返回的时候,它返回一个包含新进来的连接的 SocketChannel。因此,accept()方法会一直阻塞到有新连接到达。
while(true){
SocketChannel socketChannel = serverSocketChannel.accept();
// do something
}
如果是非阻塞模式,accept()方法会立刻返回,如果还没有新进来的连接,返回的将是null。 因此,需要检查返回的SocketChannel是否是null.
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(80));
serverSocketChannel.configureBlocking(false);
while(true) {
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel != null) {
//do something
}
}
DatagramChannel是一个能收发UDP包的通道。因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。
DatagramChannel channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(9000));
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
channel.receive(buf);
String newData = "Some data";
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
int bytesSent = channel.send(buf, new InetSocketAddress("www.xxx.com", 9000));
由于UDP是无连接的,连接到特定地址并不会像TCP通道那样创建一个真正的连接,而是锁住DatagramChannel ,让其只能从特定地址收发数据。
channel.connect(new InetSocketAddress("www.xxx.com", 9000));
当连接后,也可以使用read()和write()方法,就像在用传统的通道一样,只是在数据传送方面没有任何保证。