Java NIO(五)-FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel[译]

Java NIO(五)-FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel

一、FileChannel

Java NIO FileChannel是连接到文件的通道。使用FileChannel,可以从文件中读取数据,并将数据写入文件。 Java NIO FileChannel类是标准Java IO API读取文件的可替代方法。
FileChannel不能设置为非阻塞模式,它只能使用阻塞模式运行

1.打开一个文件通道(FileChannel)

在使用FileChannel之前,必须先将其打开。FileChannel无法直接打开。你需要通过InputStream,OutputStream或RandomAccessFile获取FileChannel。 以下是通过RandomAccessFile打开FileChannel的方法:

RandomAccessFile aFile     = new RandomAccessFile("data/nio-data.txt", "rw");
FileChannel      inChannel = aFile.getChannel();

2.从文件通道中读取数据

可以通过read()方法读取数据

ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf);

首先分配缓冲区。从FileChannel读取的数据被读入缓冲区。
其次,调用FileChannel.read()方法。这个方法将数据从FileChannel读入缓冲区。read()方法返回的int告诉缓冲区中有多少字节。 如果返回-1,则到达文件结尾。

3.写数据到文件通道

可以通过FileChannel.write()方法将数据写入文件通道

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.write()被使用在while循环中调用。这里无法保证有多少字节的数据写入到FileChannel中,因此我们重复调用write方法指导缓冲区中没有字节可以写入。

4. 关闭文件通道

可以通过close()方法关闭文件通道

channel.close();

5.文件通道的位点(FileChannel Position)

你可以指定一个位置往FileChannel中进行读写操作。可以通过调用position方法获取FileChannel中的当前位置。也可以通过position(long pos)方法设置当前的位置。

//获取当前位置
long pos channel.position();
//设置当前位置
channel.position(pos +123);

如果你把位置设置到了文件的末尾后面,尝试去从通道中读取数据的时候,将会返回-1来表示到达文件末尾。这时候,如果你往通道中写入数据,文件将会被扩大去适应位点(position)然后写入数据。这可能造成“文件空洞”,在磁盘上的物理文件在写入的数据中有一个缺口。

6.获取文件通道大小

size方法可以获取通道大小

long fileSize = channel.size(); 

7.截断文件通道

可以调用FileChannel.truncate()方法截断文件。可以给定一个长度对文件通道进行截断:

//将文件截断为大小为1024字节
channel.truncate(1024);

8. 文件通道Force方法

FileChannel.force()方法将所有未写入的数据从通道刷新到磁盘。出于性能原因,操作系统可能会将数据缓存在内存中,因此在调用force()方法之前,无法保证写入通道的数据实际写入磁盘。force()方法将boolean作为参数,告诉是否应该刷新文件元数据(权限等)。
这是一个刷新数据和元数据的示例:

channel.force(true);

二、SocketChannel

Java NIO SocketChannel是连接到TCP网络套接字的通道。它是Java NIO相当于Java Networking的套接字。可以通过两种方式创建SocketChannel:

  1. 打开SocketChannel并连接到Internet上的某个服务器。
  2. 当传入连接到达ServerSocketChannel时,可以创建SocketChannel。

1.打开SocketChannel

SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://localhost", 80));

2.关闭SocketChannel

socketChannel.close();  

3.从SocketChannel中读取数据

可以通过其中的一个read方法读取数据:

//创建一个48字节的缓存区
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf);

首先分配缓冲区。从SocketChannel读取的数据被读入缓冲区。
其次,调用SocketChannel.read()方法。 此方法将数据从SocketChannel读入Buffer。read()方法返回的int告诉缓冲区中有多少字节。 如果返回-1,则到达流的末尾(连接已关闭)。

4.写入数据到SocketChannel

可以通过write()方法写入数据,buffer作为变量:


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);
}

因为无法保证有多少数据写入到SocketChannel中,所有在while循环中重复调用write方法知道字节都被写完。

5.非阻塞模式

可以设置SocketChannel为非阻塞模式,可以通过调用connect(),read(),write()方法来实现异步模式。

connect()

如果SocketChannel处于非阻塞模式,调用connect()方法,这个方法可能会在建立连接之前返回。 要确定是否建立了连接,可以调用finishConnect()方法,如下:

socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));

while(! socketChannel.finishConnect() ){
    //等待,或者做一些别的处理 
}
write()

在非阻塞模式下,write()方法可能在没有写入任何内容的情况下返回。因此,需要在循环中调用write()方法。 由于之前的案例中有提到,这里就不写案例了。

read()

在非阻塞模式下,read()方法可能在没有读取任何数据的情况下返回。因此,需要注意返回的int,它告诉读取了多少字节。

使用selector选择器进行非阻塞处理

TODO //后续详解

三、ServerSocketChannel

Java NIO ServerSocketChannel是一个可以监听传入TCP连接的通道,就像标准Java网络模块中的ServerSocket一样。 ServerSocketChannel类位于java.nio.channels包中。
案例:

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.socket().bind(new InetSocketAddress(9999));

while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();

    //使用socketChannel做一些事情
}

1.打开ServerSocketChannel

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

2.关闭ServerSocketChannel

serverSocketChannel.close();

3.监听访问的连接

通过调用ServerSocketChannel.accept()方法来监听传入连接。当accept()方法返回时,它返回带有传入连接的SocketChannel。因此,accept()方法将阻塞,直到传入的连接到达。

由于通常不想只听一个连接,所以在while循环中调用accept():

while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();
}

当然,你会在while循环中使用一些其他的停止while循环的参数而不是true。

4.非阻塞模式

ServerSocketChannel可以设置为非阻塞模式。在非阻塞模式下,accept()方法立即返回,如果没有到达传入连接,则返回null。 因此,必须检查返回的SocketChannel是否为空。

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);

while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();

    if(socketChannel != null){
        //判断空后再使用socketChannel进行处理
        }
}

四、DatagramChannel

Java NIO DatagramChannel是可以发送和接收UDP数据包的通道。由于UDP是一种无连接的网络协议,因此不能像在其他通道中那样默认读取和写入DatagramChannel。而是发送和接收数据包。

1.打开DatagramChannel

DatagramChannel channel = DatagramChannel.open();
//绑定端口9999
channel.socket().bind(new InetSocketAddress(9999));

2.接受数据

ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();

channel.receive(buf);

receive方法将接收到的数据包的内容复制到给定的Buffer中。如果接收的数据包包含的数据多于缓冲区可以包含的数据,则会以静默方式丢弃剩余的数据。

3.发送数据

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("localhost", 80));

例子中,通过UDP协议将字符串发送到主机为localhost的80端口的服务上。但是没有服务对这个端口进行监听,所以什么都不会发生。由于UDP不对数据传送做出任何保证,因此不会通知是否收到了发送数据包。

4.连接到指定的地址

将DatagramChannel“连接”到网络上的特定地址是可行的。由于UDP是无连接的,因此这种连接到地址的方式不会像TCP通道那样创建真正的连接。相反,它会锁定DatagramChannel,因此只能从一个特定地址发送和接收数据包。

channel.connect(new InetSocketAddress("localhost", 80));

连接后,还可以使用read和write方法,就像使用传统通道一样。对交付已发送数据没有任何保证。

//读数据
int bytesRead = channel.read(buf);  
//写数据
int bytesWritten = channel.write(buf);

你可能感兴趣的:(Java)