一般来讲, 全部的 NIO 的 I/O 操做都是从 Channel 开始的. 一个 channel 相似于一个 stream.
java Stream 和 NIO Channel 对比java
咱们能够在同一个 Channel 中执行读和写操做, 然而同一个 Stream 仅仅支持读或写.segmentfault
Channel 能够异步地读写, 而 Stream 是阻塞的同步读写.缓存
Channel 老是从 Buffer 中读取数据, 或将数据写入到 Buffer 中.服务器
Channel 类型有:网络
FileChannel, 文件操做dom
DatagramChannel, UDP 操做异步
SocketChannel, TCP 操做socket
ServerSocketChannel, TCP 操做, 使用在服务器端.
这些通道涵盖了 UDP 和 TCP网络 IO以及文件 IO.code
基本的 Channel 使用例子:server
public static void main( String[] args ) throws Exception { RandomAccessFile aFile = new RandomAccessFile("/Users/xiongyongshun/settings.xml", "rw"); FileChannel inChannel = aFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); while (bytesRead != -1) { buf.flip(); while(buf.hasRemaining()){ System.out.print((char) buf.get()); } buf.clear(); bytesRead = inChannel.read(buf); } aFile.close(); }
FileChannel 是操做文件的Channel, 咱们能够经过 FileChannel 从一个文件中读取数据, 也能够将数据写入到文件中.注意
, FileChannel 不能设置为非阻塞模式.
RandomAccessFile aFile = new RandomAccessFile("test.txt", "rw"); FileChannel inChannel = aFile.getChannel();
ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf);
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 的操做完成后, 必须将其关闭
channel.close();
long pos channel.position(); channel.position(pos +123);
咱们能够经过 channel.size()获取关联到这个 Channel 中的文件的大小. 注意, 这里返回的是文件的大小, 而不是 Channel 中剩余的元素个数.
channel.truncate(1024);
将文件的大小截断为1024字节.
咱们能够强制将缓存的未写入的数据写入到文件中:
channel.force(true);
SocketChannel 是一个客户端用来进行 TCP 链接的 Channel.
建立一个 SocketChannel 的方法有两种:
打开一个 SocketChannel, 而后将其链接到某个服务器中
当一个 ServerSocketChannel 接受到链接请求时, 会返回一个 SocketChannel 对象.
SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress("http://example.com", 80));
socketChannel.close();
ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = socketChannel.read(buf);
若是 read()返回 -1, 那么表示链接中断了.
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 为异步模式, 这样咱们的 connect, read, write 都是异步的了.
socketChannel.configureBlocking(false); socketChannel.connect(new InetSocketAddress("http://example.com", 80)); while(! socketChannel.finishConnect() ){ //wait, or do something else... }
在异步模式中, 或许链接尚未创建, connect 方法就返回了, 所以咱们须要检查当前是不是链接到了主机, 所以经过一个 while 循环来判断.
在异步模式下, 读写的方式是同样的.
在读取时, 由于是异步的, 所以咱们必须检查 read 的返回值, 来判断当前是否读取到了数据.
ServerSocketChannel 顾名思义, 是用在服务器为端的, 能够监听客户端的 TCP 链接, 例如:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(9999)); while(true){ SocketChannel socketChannel = serverSocketChannel.accept(); //do something with socketChannel... }
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.close();
咱们可使用ServerSocketChannel.accept()方法来监听客户端的 TCP 链接请求, accept()方法会阻塞, 直到有链接到来, 当有链接时, 这个方法会返回一个 SocketChannel 对象:
while(true){ SocketChannel socketChannel = serverSocketChannel.accept(); //do something with socketChannel... }
在非阻塞模式下, accept()是非阻塞的, 所以若是此时没有链接到来, 那么 accept()方法会返回null:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(9999)); serverSocketChannel.configureBlocking(false); while(true){ SocketChannel socketChannel = serverSocketChannel.accept(); if(socketChannel != null){ //do something with socketChannel... } }
DatagramChannel 是用来处理 UDP 链接的.
DatagramChannel channel = DatagramChannel.open(); channel.socket().bind(new InetSocketAddress(9999));
ByteBuffer buf = ByteBuffer.allocate(48); buf.clear(); channel.receive(buf);
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("example.com", 80));
由于 UDP 是非链接的, 所以这个的 connect 并非向 TCP 同样真正意义上的链接, 而是它会讲 DatagramChannel 锁住, 所以咱们仅仅能够从指定的地址中读取或写入数据.
channel.connect(new InetSocketAddress("example.com", 80));
本文由 yongshun 发表于我的博客, 采用署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议.非商业转载请注明做者及出处. 商业转载请联系做者本人Email: yongshun1228@gmail.com本文标题为: Java NIO 的前生今世 之二 NIO Channel 小结本文连接为: segmentfault.com/a/1190000006824107