NIO缓存区(4)之通道、管道

文件通道

FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下。 
打开FileChannel 

我们无法直接打开一个FileChannel,需要通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例

从FileChannel读取数据 
调用多个read()方法之一从FileChannel中读取数据。如: 

Java代码 

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

2.   int bytesRead = inChannel.read(buf);  

首先,分配一个Buffer。从FileChannel中读取的数据将被读到Buffer中。 

向FileChannel写数据 
使用FileChannel.write()方法向FileChannel写数据,该方法的参数是一个Buffer。如: 

Java代码 

1.   String newData = "New String to write to file..." + System.currentTimeMillis();  

2.     

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

4.   buf.clear();  

5.   buf.put(newData.getBytes());  

6.     

7.   buf.flip();  

8.     

9.   while(buf.hasRemaining()) {  

10.      channel.write(buf);  

11.  }  

注意FileChannel.write()是在while循环中调用的。因为无法保证write()方法一次能向FileChannel写入多少字节,因此需要重复调用write()方法,直到Buffer中已经没有尚未写入通道的字节。 
关闭FileChannel 
用完FileChannel后必须将其关闭。如: 

Java代码 

1.   channel.close();  

FileChannel的position方法 

可以通过调用position()方法获取FileChannel的当前位置。 
也可以通过调用position(long pos)方法设置FileChannel的当前位置。 
这里有两个例子: 

Java代码 

1.   long pos = channel.position();  

2.   channel.position(pos +123);  

FileChannel的size方法 
FileChannel实例的size()方法将返回该实例所关联文件的大小。如: 

Java代码 

1.   long fileSize = channel.size();  

FileChannel的truncate方法 
可以使用FileChannel.truncate()方法截取一个文件。截取文件时,文件将中指定长度后面的部分将被删除。

Java代码 

1.   channel.truncate(1024);  

这个例子截取文件的前1024个字节。 
FileChannel的force方法 
FileChannel.force()方法将通道里尚未写入磁盘的数据强制写到磁盘上。
force()方法有一个boolean类型的参数,指明是否同时将文件元数据(权限信息等)写到磁盘上。 
下面的例子同时将文件数据和元数据强制写到磁盘上: 

Java代码 

1.   channel.force(true);  

Socket 通道

Java NIO中的SocketChannel是一个连接到TCP网络套接字的通道。可以通过以下2种方式创建SocketChannel: 

·        打开一个SocketChannel并连接到互联网上的某台服务器。

·        一个新连接到达ServerSocketChannel时,会创建一个SocketChannel。

打开 SocketChannel 
下面是SocketChannel的打开方式: 

Java代码 

1.   SocketChannel socketChannel = SocketChannel.open();  

2.   socketChannel.connect(new InetSocketAddress("http://jenkov.com"80));  

关闭 SocketChannel 
当用完SocketChannel之后调用SocketChannel.close()关闭SocketChannel: 

Java代码 

1.   socketChannel.close();  

connect() 
如果SocketChannel在非阻塞模式下,此时调用connect(),该方法可能在连接建立之前就返回了。为了确定连接是否建立,可以调用finishConnect()的方法。像这样: 

Java代码 

1.   socketChannel.configureBlocking(false);  

2.   socketChannel.connect(new InetSocketAddress("http://jenkov.com"80));  

3.     

4.   while(! socketChannel.finishConnect() ){  

5.       //wait, or do something else...  

6.   }  

ServerSocket 通道

Java代码 

1.   ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();  

2.     

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

4.     

5.   while(true){  

6.       SocketChannel socketChannel =  

7.               serverSocketChannel.accept();  

8.     

9.       //do something with socketChannel...  

10.  }  

打开ServerSocketChannel 
通过调用 ServerSocketChannel.open() 方法来打开ServerSocketChannel.如: 

Java代码 

1.   ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();  

关闭ServerSocketChannel 
通过调用ServerSocketChannel.close() 方法来关闭ServerSocketChannel. 如: 

Java代码 

1.   serverSocketChannel.close();  

监听新进来的连接 
通过 ServerSocketChannel.accept() 方法监听新进来的连接。当 accept()方法返回的时候,它返回一个包含新进来的连接的 SocketChannel。因此,accept()方法会一直阻塞到有新连接到达。 
通常不会仅仅只监听一个连接,在while循环中调用 accept()方法. 如下面的例子: 

Java代码 

1.   while(true){  

2.       SocketChannel socketChannel =  

3.               serverSocketChannel.accept();  

4.     

5.       //do something with socketChannel...  

6.   }  

当然,也可以在while循环中使用除了true以外的其它退出准则。 
非阻塞模式 
ServerSocketChannel可以设置成非阻塞模式。在非阻塞模式下,accept() 方法会立刻返回,如果还没有新进来的连接,返回的将是null。 因此,需要检查返回的SocketChannel是否是null。如: 

Java代码 

1.   ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();  

2.     

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

4.   serverSocketChannel.configureBlocking(false);  

5.     

6.   while(true){  

7.       SocketChannel socketChannel =  

8.               serverSocketChannel.accept();   

9.       if(socketChannel != null){  

10.          //do something with socketChannel...  

11.      }  

12.  }  

Datagram 通道

打开 DatagramChannel 
下面是 DatagramChannel 的打开方式: 

Java代码 

1.   DatagramChannel channel = DatagramChannel.open();  

2.   channel.socket().bind(new InetSocketAddress(9999));  

这个例子打开的 DatagramChannel可以在UDP端口9999上接收数据包。 
接收数据 
通过receive()方法从DatagramChannel接收数据,如: 

Java代码 

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

2.   buf.clear();  

3.   channel.receive(buf);  

receive()方法会将接收到的数据包内容复制到指定的Buffer. 如果Buffer容不下收到的数据,多出的数据将被丢弃。 
发送数据 
通过send()方法从DatagramChannel发送数据,如: 

Java代码 

1.   String newData = "New String to write to file..." + System.currentTimeMillis();  

2.     

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

4.   buf.clear();  

5.   buf.put(newData.getBytes());  

6.   buf.flip();  

7.     

8.   int bytesSent = channel.send(buf, new InetSocketAddress("jenkov.com"80));  

连接到特定的地址 
可以将DatagramChannel“连接”到网络中的特定地址的。由于UDP是无连接的,连接到特定地址并不会像TCP通道那样创建一个真正的连接。而是锁住DatagramChannel ,让其只能从特定地址收发数据。 
这里有个例子: 

Java代码 

1.   channel.connect(new InetSocketAddress("jenkov.com"80));  

Java代码 

1.   int bytesRead = channel.read(buf);  

2.   int bytesWritten = channel.write(but);  

管道(Pipe)

这里是Pipe原理的图示: 

创建管道 
通过Pipe.open()方法打开管道。例如: 

Java代码 

1.   Pipe pipe = Pipe.open();  

向管道写数据 
要向管道写数据,需要访问sink通道。像这样: 

Java代码 

1.   Pipe.SinkChannel sinkChannel = pipe.sink();  

通过调用SinkChannel的write()方法,将数据写入SinkChannel,像这样: 

Java代码 

1.   String newData = "New String to write to file..." + System.currentTimeMillis();  

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

3.   buf.clear();  

4.   buf.put(newData.getBytes());  

5.     

6.   buf.flip();  

7.     

8.   while(buf.hasRemaining()) {  

9.       <b>sinkChannel.write(buf);</b>  

10.  }  

从管道读取数据 
从读取管道的数据,需要访问source通道,像这样: 

Java代码 

1.   Pipe.SourceChannel sourceChannel = pipe.source();  

调用source通道的read()方法来读取数据,像这样: 

Java代码 

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

2.     

3.   int bytesRead = inChannel.read(buf);  

read()方法返回的int值会告诉我们多少字节被读进了缓冲区。

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值