NIO的特性:它以块为基本单位处理数据,所有的数据都要通过缓冲区(Buffer)来进行传输。它有一个用来作为原始I/O操作的抽象通道(Channel)并提供了Selector的异步网络接口。且支持将文件映射到内存,以大幅提高I/O效率。
缓冲区中有3个重要的参数:
position(位置):即缓冲区的位置,指缓冲区指针到哪个位置了。
capactiy(容量):缓冲区的总上限,如ByteBuffer.allocate(2048),则认为产生的这个bytebuffer的容量为2048
limit(上限):实际存入缓冲区数据所占的容量,也称为实际上限。
常用方法:
filp():将position置为0,将limit置为原position位置。经过此操作的buffer数据才能被正常读取(读写转换)。
clear():将position置为0,limit设置为容量大小,并清除mark标志位。效果是清空buffer
rewind():将position置为0,并清除标志位
文件映射到内存的方法:
RandomAccessFile raf = new RandomAccessFile("C:\\test.dat", "rw");
FileChannel fc = raf.getChannel();
//将文件映射到内存中
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, raf.length()); while(mbb.hasRemaining()){
System.out.print((char)mbb.get()); }
mbb.put(0,(byte)98); //修改文件
raf.close();
正文:
Nio非阻塞网络通信的工作原理为,由一个线程监听多个客户端,由selector选择已完成数据准备工作的客户端,以对请求做出处理。由一个线程来监听多个客户端的方式能够省下来大量的线程资源。
以下是模拟一次计算请求的代码:
服务端:
public class NioServer {
private Selector selector;
private ByteBuffer recive = ByteBuffer.allocate(2048);
private ByteBuffer send = ByteBuffer.allocate(2048);
public NioServer(int port) throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
// 设置非阻塞
ssc.configureBlocking(false);
ServerSocket socket = ssc.socket();
socket.bind(new InetSocketAddress(port));
selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
}
/**
* 启动服务器
*
* @throws IOException
*/
public void startup() throws IOException {
System.out.println("=======Nio Server is started!=======");
for (;;) {
int size = selector.select();
if (size > 0) {
Set<SelectionKey> keys = selector.selectedKeys();
for (Iterator<SelectionKey> iterator = keys.iterator(); iterator.hasNext();) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
processor(key);
}
}
}
}
/**
* 停止服务器
*/
public void shutdown() {
System.out.println("=======Nio Server is stoped!=======");
System.exit(0);
}
/**
* 消息处理
*
* @throws IOException
*/
public void processor(SelectionKey key) throws IOException {
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel accept = server.accept();
accept.configureBlocking(false);
// 改变监听状态
accept.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
recive.clear(); // 在读取数据之前要保证缓冲区是空的
int size = client.read(recive); //读取完成后返回-1
if(size == -1) {
//如果没读出来东西则不做任何操作
} else {
client.register(selector, SelectionKey.OP_WRITE);
}
} else if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
int value = arithmetic(new String(recive.array())); //计算结果[业务部分]
byte[] result = String.valueOf(value).getBytes();
send.clear();
send.put(result);
send.flip();
// 返回结果
client.write(send);
client.register(selector, SelectionKey.OP_READ); //如有大量数据要处理,设置此项 循环处理。
}
}
/**
* 计算结果
*
* @return
*/
private int arithmetic(String data) {
//假设这里面有一堆操作
System.out.println("客户端数据:"+data);//结果为空,转换缓冲区没报错
return 1;
}
public static void main(String[] args) {
try {
NioServer nioServer = new NioServer(9900);
nioServer.startup();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端:
public class NioClient {
private int port;
private ByteBuffer recive = ByteBuffer.allocate(2048);
private ByteBuffer send = ByteBuffer.allocate(2048);
public NioClient(int port) throws IOException {
this.port = port;
}
/**
* 发送数据并等待运算结果
*
* @param obj
* @return
*/
public Object send(Object obj) throws IOException {
SocketChannel sc = SocketChannel.open();
sc.configureBlocking(false);
Selector selector = Selector.open();
sc.connect(new InetSocketAddress(port));
sc.register(selector, SelectionKey.OP_CONNECT);
Object result = null;
boolean isDone = false;
for (;;) {
if (selector.select() == 0) {
continue;
}
Set<SelectionKey> keys = selector.selectedKeys();
for (Iterator<SelectionKey> iterator = keys.iterator(); iterator.hasNext();) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
sc = (SocketChannel) key.channel();
if (key.isConnectable()) {
if (sc.isConnectionPending()) {
sc.finishConnect();
sc.register(selector, SelectionKey.OP_WRITE);
}
} else if (key.isReadable()) {
recive.clear();
sc.read(recive);
result = new String(recive.array());
System.out.println("done!");
sc.register(selector, SelectionKey.OP_WRITE);
isDone = true;
sc.close();
} else if (key.isWritable()) {
byte[] datas = ((String)obj).getBytes();
send.clear();
send.put(datas);
//装载完数据要恢复缓冲区标志位,不然服务端会在读取时出现问题
send.flip();
sc.write(send);
sc.register(selector, SelectionKey.OP_READ);
}
} //iterator
if(isDone == true) {
break;
}
} //for true
return result;
}
public static void main(String[] args) {
try {
NioClient client = new NioClient(9900); //连接四则运算服务器
Object result = client.send("1+1");
System.out.println("计算结果为:"+ String.valueOf(result));
} catch (IOException e) {
e.printStackTrace();
}
}
}
源码下载地址:http://download.csdn.net/detail/shf4715/9838492
如有错误或不合理的地方,烦请大家留言,本人会尽快改正。