Java NIO学习笔记

来源:互联网 发布:存放字符串的数组 编辑:程序博客网 时间:2024/05/19 14:17
1.java nio是非阻塞io,为所有原始类型提供buffer,提供多路非阻塞(no-blocking)的高伸缩性网络应用。java nio中主要的组件是buffer、channel、selector。
所有的IO在NIO中都是从channel开始。

在NIO中buffer的实现:
ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
LongBuffer
ShortBuffer
覆盖了基本所有基本类型。除此之外,还有MappedBuffer。
使用buffer的一般步骤:
1)分配buffer大小
2)将数据读到buffer中
3)调用flip()方法
4)读取buffer中的数据
5)调用clear方法或compact方法
重要的变量:
position、limit、capacity
buffer作为一个内存块,有个大小,就是capacity,满了之后只有清空
position在特定某个位置,当读取数据的时候,position移向下个位子
limit在写模式下,limit等于capacity;在读模式下,limit等于position,表示能读取已写的数据
重要的方法:
flip()
将position设为0
compact()
将未读数据移到开始位置,position移到最后一个未读元素后,写就不会覆盖未读数据
clear()
将position设为0
mark()
标记一个特定的位置
reset()
恢复到上个使用mark()的位置
equals()
equals不是比较两个buffer全部内容,只是比较剩余元素
compareTo()
第一个不想等的元素小于另外一个buffer中对应的元素;所有元素都想等,但第一个buffer比另外一个先消耗尽


在NIO中channel的实现:
FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel
channel和流相似,但是有些不同:
1.既可以从流读取数据,又可以写数据到channel中。但是流通常是单向的
2.channel可以异步地读取
3.channel要读数据,要先读到buffer;channel要写数据,要先写到buffer


在NIO中selector允许单线程处理多个channel。如果应用打开了多个连接,但流量很低,使用selector就会很方便。例如聊天室。


当需要将传输数据分开处理,例如webservice报文中的消息体和消息头。这是可以将数据放到不同的buffer。
1)将数据从channel分散到不同的buffer
ByteBuffer header = ByteBuffer.allocate(128);ByteBuffer body = ByteBuffer.allocate(128);ByteBuffer[] bufferArray = {header, body};channel.read(bufferArray);

2)将不同的buffer聚合到一个channel中
ByteBuffer head = ByteBuffer.allocate(128);ByteBuffer body = ByteBuffer.allocate(128);ByteBuffer[] bufferArray = {header, body};channel.write(bufferArray);

channel间的数据转移
主要是transferFrom()和transferTo()
RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");FileChannel fromChannel = fromFile.getChannel();RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");FileChannel toChannel = toFile.getChannel();long position = 0;long count = fromChannel.size();toChannel.transferFrom(position, count, fromChannel);

RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");FileChannel fromChannel = fromFile.getChannel();RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");FileChannel toChannel = toFile.getChannel();long position = 0;long count = fromChannel.size();fromChannel.transferTo(toChannel);


selector的使用
selector能够处理多个channel
使用selector的步骤:
1)selector的创建
2)向selector注册通道
3)轮询注册时间的到来
Selector selector = Selector.open();channel.configureBlocking(false);SelectionKey key = channel.register(selector, SelectorKey.OP_READ);while (true) {  int readyChannels = selector.select();  if (readyChannels == 0) {    Set selectedKeys = selector.selectedKeys();    Iterator keyIterator = selectedKeys.iterator();while (KeyIterator.hasNext()) {SelectionKey key = keyIterator.next();if (key.isAcceptable()) {//todo }else if (key.isConnectable()) {//TODO}else if (key.isReadable()) {//TODO}else if (key.isWritable()) {//TODO}}  }}


FileChannel的使用
FileChannel一定是阻塞的模式下的,这个一定要注意。
读文件:
RandomAccessFile file = new RandomAccessFile("file.txt", "rw");FileChannel fileChannel = file.getChannel();ByteBuffer buf = new ByteBuffer.allocate(128);buf.flip();int byteRead = fileChannel.read(buf);file.close();

写文件:
RandomAccessFile file = new RandomAccessFile("file.txt", "rw");FileChannel channel = file.getChannel();String content = "i want to learn java nio";ByteBuffer buf = ByteBuffer.allocate(128);buf.clear();buf.put(content.getByte());buf.flip();while(buf.hasRemaining()) (channel.write(buf) ;)channel.close();

根据偏移量读内容:
long pos = channel.position();chanenl.position(pos + 100);

截断内容
channel.truncate(1024);

强制写到硬盘中
channel.forece();




使用TCP

创建SocketChannel的方式:
1)打开一个SocketChannel连接到互联网中的某台机器
2)一个新的连接到达ServerSocketChannel时,会创建一个SocketChannel

从SocketChannel读取数据:
SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("http://facebook.com", 80));ByteBuffer buf = ByteBuffer.allocate(128);int byteRead = socketChannel.read(buf);socketChannel.close();

写数据到SocketChannel:
SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("http://facebook.com", 80));String content = "i want to write some data";ByteBuffer bu = ByteBuffer.allocate(128);buf.clear();buf.put(content.getByte());buf.flip();while(buf.hasRemainning()) {  channel.write(buf);}socketChannel.close();

非阻塞模式
当调用channel.configureBlocking(false)之后,就可以在异步的状态下调用connect()、read()、write()
非阻塞下的connect
因为非阻塞下,不会等待有结果就返回了,所以为了确定连接是否建立,可以调用finishConnect()检测是否连接成功
socketChannel.configureBlocking(false);socketChannel.connect("www.facebook.com", 80);while (!socketChannel.finishConnect()) {  //}

同理类似write()和read()




ServerSocketChannel

ServerSocketChannel可以监听新进来的TCP连接。
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket(().bind(new InetSocketAddress(9999));while (true) {  SocketChannel socketChannel = serverSocketChannel.accept();}

直到阻塞有新的连接到达

非阻塞模式
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(9999));serverSocketChannel.configureBlocking(false);while(true) {  SocketChannel socketChannel = ServerSocketChannel.accept();}



DatagramChannel

因为UDP是无连接的网络协议,故不能像其他通道那样读取和写入。它发送的是数据包。

DatagramChannel channel = DatagramChannel.open();chanenl.socket().bind(new InetSocketAddress(9999));//接受数据ByteBuffer buf = ByteBuffer.allocate(128);buf.clear();channel.receive(buf);//发送数据String content = "i want to learn something.";ByteBuffer buf = ByteBuffer.allocate(128);buf.clear();buf.put(content.getByte());buf.flip();int byteSent = channel.send(buf, new InetSocketAddress("www.facebook.com", 80));//连接到特定的地址//因为UDP是无连接的,连接到特定地址并不会向TCP通道那样创建真正的连接。而是锁住DatagramChannel,让其只能从特定地址收发数据,注意数据的收发没有任何保证。//读int byteRead = channel.read(buf);//写int byteWritten = channel.write(buf);



pipe

管道是2个线程之间单向的数据连接。而管道中有sink管道和source管道。数据被写入sink管道,从source管道读取。
使用步骤:
1.创建管道
2.向管道写数据
3.从管道读取数据
Pipe pipe = Pipe.open();// 写数据到sinkChanenlPipe.SinkChannel sinkChannel = pipe.sink();String content = "i want to learn";ByteBuffer buf = ByteBuffer.allocate(128);buf.clear();buf.put(content.getByte());buf.flip();while(buf.hasRemaining()) {  sinkChannel.write(buf);}//从sourceChannel读取数据Pipe.SourceChannel sourceChannel = pipe.source();ByteBuffer buf = ByteBuffer.allocate(128);int byteRead = inChannel.read(buf);

0 0
原创粉丝点击