Java 提供了NIO概念,也即是非阻塞IO概念。并引入了对文件和socket的一些封装。本文将详细介绍NIO。
Channel
Channel 本质上实现了对句柄的封装。Linux系统中用一个正式fd来描述文件或者网络套接字的唯一封装。Channel大体的意思也是对fd的封装。
Channel有四种,具体为:
ServerSocketChannel //Socket 服务器版本
SocketChannel //Socket 客户端版本
FileChannel //文件Channel
DatagramChannel //UDP
Buffer
Buffer 为读取Socket或者文件的一块内存。每个基本数据都有对应的Buffer。 但常用的只有ByteBuffer,按字节来读数据。
Buffer的方法需要灵活记忆:
Buffer的三个成员变量position, limit 和Capacity. Capacity为这块Buffer的内存大小。
读模式下postion 为当前读到buffer位置的指针,limit为可读数据。
写模式下postion 为当前写到buffer的位置,limit为capacity.
flip()函数写模式向读模式切换。
rewind()函数为读模式下,重新开始读。
clear() 清空所有数据
compact() 把没读的数据拷贝到buffer的开始。
Mark(), Reset()下一个书签功能,回到书签位置继续读。
FileChannel 实例
读文件并打印到控制台。
public static void testChannel() throws IOException
{
RandomAccessFile raf = new RandomAccessFile("/home/dumplog.py","r");
FileChannel fc = raf.getChannel();
ByteBuffer bf =ByteBuffer.allocate(16);
int len;
while((len=fc.read(bf))!=-1)
{
bf.flip();
while(bf.hasRemaining())
System.out.print((char)bf.get());
bf.clear();
}
raf.close();
fc.close();
}
SocketChannel 实例
实例接受客户端数据,并打印出来,然后返回一个数据。为了避免复杂,并没有写客户端程序,大家可以用telnet代替。telnet到这个端口,发送数据来测试。当读到的数据长度或者写的数据长度为-1时,意味着链接断开,需要关闭channel.
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NIOTest {
public static void main(String[] args) throws IOException {
Selector select = Selector.open();
ServerSocketChannel listenChannel = ServerSocketChannel.open();
listenChannel.configureBlocking(false);
listenChannel.socket().bind(new InetSocketAddress(8080));
listenChannel.socket().setReuseAddress(true);
listenChannel.register(select, SelectionKey.OP_ACCEPT);
while(true)
{
select.select();
Iterator<SelectionKey> it = select.selectedKeys().iterator();//select.keys().iterator();
while(it.hasNext())
{
SelectionKey key = it.next();
it.remove();
if(key.isAcceptable())
{
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = channel.accept();
clientChannel.configureBlocking(false);
//clientChannel.write(ByteBuffer.wrap(new String("hello,trender").getBytes()));
clientChannel.register(select, SelectionKey.OP_READ);
}
else if(key.isReadable())
{
SocketChannel channel = (SocketChannel) key.channel();
// \u521b\u5efa\u8bfb\u53d6\u7684\u7f13\u51b2\u533a
ByteBuffer buffer = ByteBuffer.allocate(1024);
try
{
int num = channel.read(buffer);
if(num==-1)
{
channel.close();
continue;
}
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("\u670d\u52a1\u7aef\u6536\u5230\u4fe1\u606f\uff1a"+msg);
ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
channel.write(outBuffer);
channel.register(select, SelectionKey.OP_WRITE);
}
catch(Exception e)
{
System.out.print(e);
continue;
}
}
else if (key.isWritable())
{
SocketChannel channel = (SocketChannel) key.channel();
try
{
int len =channel.write(ByteBuffer.wrap(new String("hello trender\n").getBytes()));
if(len<0)
{
channel.close();
continue;
}
channel.register(select, SelectionKey.OP_READ);
}catch(Exception e)
{
System.out.print(e);
}
}
}
}
}
}
小结
本文只是做了最简单的概述,可以让大家快速了解JAVA NIO,代码中还用到了Selector,其实和unix中select 函数一个道理,并没有深入。
建议从事网络相关开发,还需多看代码,最后把java.nio看代码通读下。