一. Channel概述
-
作用:Channel表示IO源与目标打开的连接。Channel类似于传统的"流",只不过Channel本身不能直接访问数据,Channel只能与Buffer进行交互。
-
与传统IO流比较:
-
传统的IO流:CPU处理IO,性能损耗太大,读写单向的
-
Channel:不需要向CPU申请权限,专门用于IO,读写可以双向,可以异步读写
-
-
Channel的实现:
-
FileChannel :从文件中读写数据。
-
SocketChannel:能通过TCP读写网络中的数据。
-
ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
-
DatagramChannel: 能通过UDP读写网络中的数据。
-
二. FileChannel(*注:请先点击查看第三部分Buffer,再继续)
-
对文件内容进行解析
-
步骤:
-
打开FileChannel
-
创建buffer对象
-
从FileChannel中读取数据
-
循环读取buffer
-
关闭file对象和FileChannel对象
-
-
实例:
/** * 用filechannel进行文件内容解析 */ private static void fileChannelTest() { RandomAccessFile aFile = null; FileChannel inChannel=null; try { //1.打开FileChannel aFile = new RandomAccessFile("data/nio-data.txt", "rw"); inChannel = aFile.getChannel(); //2.创建buffer对象 ByteBuffer buf = ByteBuffer.allocate(48); //3.从FileChannel读取数据:把channel中的数据读取到buffer中,bytesRead表示有多少字节读到了buffer中 int bytesRead = inChannel.read(buf); //4.循环读取buffer while (bytesRead != -1) { //4.1.将buffer从写切换成读 buf.flip(); while(buf.hasRemaining()){ //4.2.获取buf中的字节,转换成char System.out.print((char) buf.get()); } //4.3.清除buffer buf.clear(); //4.4.再次从FileChannel读取数据 bytesRead = inChannel.read(buf); } //5.关闭file对象和channel对象 aFile.close(); inChannel.close(); } catch (IOException e) { e.printStackTrace(); } }
-
-
复制文件
-
步骤:
-
得到源文件通道
-
得到目标文件通道
-
设置position和count
-
将源文件通道数据写入目标文件通道
-
关闭FileChannel对象
-
-
实例:
/** * 用filechannel进行文件复制 */ private static void fileCopyWithFileChannel(){ try { RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw"); //1.得到源文件通道 FileChannel fromChannel = fromFile.getChannel(); RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw"); //2.得到目标文件通道 FileChannel toChannel = toFile.getChannel(); //3.设置position和count long position = 0; long count = fromChannel.size(); //4.将源文件通道的数据写入到目标文件通道 toChannel.transferFrom(fromChannel, position, count); fromFile.close(); toFile.close(); }catch (IOException e){ e.printStackTrace(); } }
-
-
其他FileChannel方法
-
FileChannel的position方法
-
概述:有时可能需要在FileChannel的某个特定位置进行数据的读/写操作。可以通过调用position()方法获取FileChannel的当前位置。也可以通过调用position(long pos)方法设置FileChannel的当前位置。
-
实例:
//获取position long pos = channel.position(); //设置position channel.position(pos +123);
-
-
FileChannel的size方法
-
概述:FileChannel实例的size()方法将返回该实例所关联文件的大小
-
实例:
//获取实例所关联文件的大小 long fileSize = channel.size();
-
-
FileChannel的truncate方法
-
概述:可以使用FileChannel.truncate()方法截取一个文件。截取文件时,文件将中指定长度后面的部分将被删除
-
实例:
//截取文件的前1024个字节。 channel.truncate(1024);
-
-
FileChannel的force方法
-
概述:方法将通道里尚未写入磁盘的数据强制写到磁盘上。出于性能方面的考虑,操作系统会将数据缓存在内存中,所以无法保证写入到FileChannel里的数据一定会即时写到磁盘上。要保证这一点,需要调用force()方法。
-
实例:
//同时将文件数据和元数据强制写到磁盘上 channel.force(true);
-
-
三. SocketChannel
-
socket通信--客户端(阻塞模式下)
-
步骤
-
打开 SocketChannel
-
新建读,写buffer
-
写入 SocketChannel
-
读取 SocketChannel
-
-
实现
//“WebClient客户端”需要配合“WebServer服务端”测试 public class WebClient { public static void main(String[] args) throws IOException { try { //1.打开SocketChannel SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress("127.0.0.1", 8000)); //2.新建读,写buffer ByteBuffer writeBuffer = ByteBuffer.allocate(32); ByteBuffer readBuffer = ByteBuffer.allocate(32); //3.1.向writeBuffer中放入值 writeBuffer.put("hello".getBytes()); //3.2.将buffer从写转换成读 writeBuffer.flip(); while (true) { //3.3.使buffer的position置0 writeBuffer.rewind(); //3.4.将writeBuffer中的内容写入socketChannel socketChannel.write(writeBuffer); readBuffer.clear(); //4.读取socketChannel的内容到readBuffer socketChannel.read(readBuffer); System.out.println(new String(readBuffer.array())); } } catch (IOException e) { e.printStackTrace(); } } }
-
-
socket通信--客户端(非阻塞模式下)
-
步骤:
-
在阻塞模式的第1步添加“设置channel为非阻塞模式”
-
然后在连接的地方判断是否连接上
-
-
注意:
-
设置 SocketChannel 为非阻塞模式(non-blocking mode).设置之后,就可以在异步模式下调用connect(), read() 和write()
-
connect():如果SocketChannel在非阻塞模式下,此时调用connect(),该方法可能在连接建立之前就返回了。为了确定连接是否建立,可以调用finishConnect()的方法
-
write():非阻塞模式下,write()方法在尚未写出任何内容时可能就返回了。所以需要在循环中调用write()。前面已经有例子了,这里就不赘述了。
-
read():非阻塞模式下,read()方法在尚未读取到任何数据时可能就返回了。所以需要关注它的int返回值,它会告诉你读取了多少字节。
-
-
实现:
//设置SocketChannel为非阻塞模式 socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress("127.0.0.1", 8000)); //判断连接是否完成 while(! socketChannel.finishConnect() ){ //如果没有完成连接进入这里等待 }
-
四. ServerSocketChannel
-
socket通信--服务端(建议非阻塞模式)
-
步骤:
-
打开 SocketChannel
-
设置 serverSocketChannel非阻塞
-
新建读,写buffer
-
监听连接进来的 SocketChannel (通过 serverSocketChannel获取 socketChannel)
-
写入 SocketChannel
-
读取 SocketChannel
-
-
注意:非阻塞模式下,获取socketChannel后一定要判空,因为accpet会立即返回一个socketChannel(第4条位置)
-
实现:
public class WebServer { public static void main(String[] args) { try { //1.打开ServerSocketChannel ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1", 8000)); //2.设置非阻塞 serverSocketChannel.configureBlocking(false); //3.新建读,写buffer ByteBuffer writeBuffer = ByteBuffer.allocate(32); ByteBuffer readBuffer = ByteBuffer.allocate(32); //3.1.向writeBuffer中放入值 writeBuffer.put("hello".getBytes()); //3.2.将buffer从写转换成读 writeBuffer.flip(); while(true){ //4.监听进入的SocketChannel(获取SocketChannel) SocketChannel socketChannel = serverSocketChannel.accept(); if (socketChannel!=null){ //5.1.使buffer的position置0 writeBuffer.rewind(); //5.2.将writeBuffer中的内容写入socketChannel socketChannel.write(writeBuffer); readBuffer.clear(); //6.读取socketChannel的内容到readBuffer socketChannel.read(readBuffer); System.out.println(new String(readBuffer.array())); } } } catch (IOException e) { e.printStackTrace(); } } }
-
五. DatagramChannel
-
概述:DatagramChannel是一个能收发UDP包的通道。因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。
-
第一种方式发送UDP包
-
步骤:
-
打开DatagramChannel(在这里绑定端口的时候,参数只需要写端口,因为只是指定哪个端口可以接收)
-
新建读,写buffer
-
发送给指定的主机和端口
-
接收内容到readBuffer
-
-
实现:
public class WebClient { public static void main(String[] args) throws IOException { try { //1.打开DatagramChannel DatagramChannel datagramChannel = DatagramChannel.open(); //1.1.指定端口用来接收数据包 datagramChannel.socket().bind(new InetSocketAddress(9999)); //2.新建读,写buffer ByteBuffer writeBuffer = ByteBuffer.allocate(32); ByteBuffer readBuffer = ByteBuffer.allocate(32); //3.1.向writeBuffer中放入值 writeBuffer.put("hello".getBytes()); //3.2.将buffer从写转换成读 writeBuffer.flip(); while (true) { //3.3.使buffer的position置0 writeBuffer.rewind(); //3.4.将writeBuffer中的内容发送给指定主机和端口 datagramChannel.send(writeBuffer,new InetSocketAddress("127.0.0.1", 8000)); readBuffer.clear(); //4.接收内容到readBuffer datagramChannel.receive(readBuffer); System.out.println(new String(readBuffer.array())); } } catch (IOException e) { e.printStackTrace(); } } }
-
-
第二种方式发送UDP包
-
通常方式连接并操作(与SocketChannel相同)数据传送方面没有保障
-
注意:这种方式是通过指定特定主机的连接后才可以使用,但是并不是真的连接,只是锁住DatagramChannel
-
实现:
public class WebClient { public static void main(String[] args) throws IOException { try { //1.打开DatagramChannel DatagramChannel datagramChannel = DatagramChannel.open(); datagramChannel.connect(new InetSocketAddress("127.0.0.1", 8000)); //2.新建读,写buffer ByteBuffer writeBuffer = ByteBuffer.allocate(32); ByteBuffer readBuffer = ByteBuffer.allocate(32); //3.1.向writeBuffer中放入值 writeBuffer.put("hello".getBytes()); //3.2.将buffer从写转换成读 writeBuffer.flip(); while (true) { //3.3.使buffer的position置0 writeBuffer.rewind(); //3.4.将writeBuffer中的内容写入socketChannel datagramChannel.write(writeBuffer); readBuffer.clear(); //4.读取DatagramChannel的内容到readBuffer datagramChannel.read(readBuffer); System.out.println(new String(readBuffer.array())); } } catch (IOException e) { e.printStackTrace(); } } }
-