原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。 面向流 的 I/O 系统一次一个字节地处
理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。 一个 面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的I/O 所具有的优雅性和简单性。
NIO主要原理和适用。
NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的socketchannel告诉Selector,我们接着做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的socketchannel,然后,我们从这个Channel中读取数据,放心,包准能够读到,接着我们可以处理这些数据。
Selector内部原理实际是在做一个对所注册的channel的轮询访问,不断的轮询(目前就这一个算法),一旦轮询到一个channel有所注册的事情发生,比如数据来了,他就会站起来报告,交出一把钥匙,让我们通过这把钥匙来读取这个channel的内容。
服务器端:
package cn.com.testnio.base;
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;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* NIO是面向缓冲区的
* User: lihaoquan
*/
public class Server {
private Selector selector;
/**
* 值得注意的是 Buffer 及其子类都不是线程安全的。
*/
private ByteBuffer readBuffer = ByteBuffer.allocate(1024);//设置缓冲区大小
/**
* ConcurrentHashMap是支持并发的,所以这里不使用HashMap
*/
private Map<SocketChannel,byte[]> clientMessage
= new ConcurrentHashMap<SocketChannel,byte[]>();
/**
* 启动开关
* @throws IOException
*/
public void start() throws IOException {
//通过静态工厂生产服务端的channel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//设置非阻塞方式
serverSocketChannel.configureBlocking(false);
//绑定IP
serverSocketChannel.socket().bind(new InetSocketAddress(8080));
//创建选择器
selector = Selector.open();
//注册监听事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (!Thread.currentThread().isInterrupted()) {
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = keys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if(!key.isValid()) {
continue;
}
if(key.isAcceptable()) {
accept(key);
}else if(key.isReadable()) {
read(key);
}
//去除本次keyIterator.next()的对象,但不会对下次遍历有影响
keyIterator.remove();
}
}
}
/**
* 读入
* @param key
* @throws IOException
*/
public void read(SelectionKey key) throws IOException{
SocketChannel socketChannel = (SocketChannel) key.channel();
this.readBuffer.clear();
int numRead;
try {
/**
* 获取客户点的read操作读入数据块数量
*/
numRead = socketChannel.read(readBuffer);
}catch (Exception e) {
e.printStackTrace();
key.cancel();
socketChannel.close();
clientMessage.remove(socketChannel);
return;
}
byte[] bytes = clientMessage.get(socketChannel);
if(bytes == null) {
bytes = new byte[0];
}
if(numRead > 0) {
byte[] newBytes =new byte[bytes.length+numRead];
System.arraycopy(bytes,0,newBytes,0,bytes.length);
System.arraycopy(readBuffer.array(), 0, newBytes, bytes.length, numRead);
clientMessage.put(socketChannel, newBytes);
System.out.println("message>>>"+new String(newBytes));
}else {
String message = new String(bytes);
System.out.println("data>>>"+message);
}
}
/**
* 接收
* @param key
* @throws IOException
*/
public void accept(SelectionKey key) throws IOException {
ServerSocketChannel serverSocketChannel
= (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverSocketChannel.accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector,SelectionKey.OP_READ);
System.out.println("已成功连接新的客户端......");
}
public static void main(String[] args) throws Exception{
new Server().start();
}
}
客户端
package cn.com.testnio.base;
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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
/**
* User: lihaoquan
*/
public class Client {
/**
* 启动开关
* @throws IOException
*/
public void start() throws IOException {
//通过静态工厂生产客户端的channnel
SocketChannel socketChannel = SocketChannel.open();
//设置客户端请求为非阻塞方式
socketChannel.configureBlocking(false);
//绑定IP
socketChannel.connect(new InetSocketAddress(8080));
//创建选择器
Selector selector = Selector.open();
//注册监听事件
socketChannel.register(selector, SelectionKey.OP_CONNECT);
//键盘输入
Scanner scanner = new Scanner(System.in);
while (true) {
selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = keys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
//去除本次keyIterator.next()的对象,但不会对下次遍历有影响
keyIterator.remove();
if(key.isConnectable()) {
socketChannel.finishConnect();
socketChannel.register(selector,SelectionKey.OP_WRITE);
System.out.println("客户端已经连上服务器端!!");
break;
}else if(key.isWritable()) {
System.out.println("请输入信息!!");
String message = scanner.nextLine();
ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes());
socketChannel.write(writeBuffer);
}
}
}
}
public static void main(String[] args) throws Exception{
new Client().start();
}
}