简介
Java世界中的两类IO:IO(性能瓶颈)和NIO以及jdk1.7中要加入的增强版NIO
位置被设为 0,而且容量和上界被设为 10,刚好经过缓冲区能够容纳的最后一个字节。标记最初未定义。容量是固定的,但另外的三个属性可以在使用缓冲区时改变
public abstract class Buffer {//没有get(),put()方法,但是子类中有
public final int capacity()
public final int position()
public final Buffer position(int newPositio) //定位位置
public final int limit()
public final Buffer limit (int newLimit)//定位上界
public final Buffer mark() //将标记设为当前位置的值
public final Buffer reset() //将位置设为当前的标记值,如果标记值未定义将抛出异常
public final Buffer clear() //重置方法,将缓冲区置为填充状态即将position设置为 0。将 limit 设置为与 capacity 相同。
public final Buffer flip() //翻转方法,将缓冲区填充状态翻转成释放状态即 将 limit 设置为当前 position。将 position 设置为 0。实现细:buffer.limit(buffer.position()).position(0);
public final Buffer rewind()//不影响上界属性。只是将位置值设回 0
public final int remaining() //从当前位置到上界还剩余的元素数目
public final boolean hasRemaining()//是否已经达到缓冲区的上界
public abstract boolean isReadOnly(); //判断缓冲区是否仅可读,修改只读缓冲区将会抛出异常
}
应用实例——复制文件的操作:
socket 的阻塞模式意味着必须要做完IO 操作(包括错误)才会返回。
非阻塞模式下无论操作是否完成都会立刻返回,需要通过其他方式来判断具体操作是否成功。
public abstract class SelectableChannel extends AbstractChannel implements Channel{
public abstract SelectionKey register (Selector sel, int ops) throws ClosedChannelException;//将可选择通道注册到选择器上,返回表示二者关系的SelectionKey 对象,如果选择器关闭或者通道为阻塞模式则会抛出异常,第二个参数表示所关心的通道操作,在JDK 1.4中,有四种被定义的可选择操作:读(read),写(write),连接(connect)和接受(accept)。
public abstract boolean ( ) isRegistered( );//判断是否注册到选择器上
public abstract SelectionKey key isRegisteredFor (Selector sel);//判断是否注册到特定的选择器上
public abstract int validOps( );//获取特定的通道所支持的操作集合
public abstract void configureBlocking (boolean block) throws IOException;
public abstract boolean isBlocking( );//来配置并检查通道的阻塞模式
public abstract Object blockingLock( );
}
class=“java” name=“code”>public abstract class Selector{
- public abstract class Selector{
- public static Selector open() throws IOException;静态工厂方法来实例化Selector 对象
- public abstract boolean isOpen( );
- public abstract void close( ) throws IOException; 释放资源和设置选择键无效
- public abstract SelectionProvider provider( ); SelectionProvider对象用于创建一个Selector对象
- public abstract int select( ) throws IOException;
- public abstract int select (long timeout) throws IOException;是阻塞的,有三种条件可以停止阻塞:1)至少存在一条通道是ready I/O的;2)等待超时;3)被唤醒,如被调用wakeup。返回值即为ready I/O的通道数量
- public abstract int selectNow( ) throws IOException;是非阻塞的,返回值即为ready I/O的通道数量,无ready则返回0
- public abstract void wakeup( );
- public abstract Set keys( ); //返回已注册的键的集合
- public abstract Set selectedKeys( );//返回已选择的键的集合
- }
public abstract class Selector{ public static Selector open() throws IOException;静态工厂方法来实例化Selector 对象 public abstract boolean isOpen( ); public abstract void close( ) throws IOException; 释放资源和设置选择键无效 public abstract SelectionProvider provider( ); SelectionProvider对象用于创建一个Selector对象 public abstract int select( ) throws IOException; public abstract int select (long timeout) throws IOException;是阻塞的,有三种条件可以停止阻塞:1)至少存在一条通道是ready I/O的;2)等待超时;3)被唤醒,如被调用wakeup。返回值即为ready I/O的通道数量 public abstract int selectNow( ) throws IOException;是非阻塞的,返回值即为ready I/O的通道数量,无ready则返回0 public abstract void wakeup( ); public abstract Set keys( ); //返回已注册的键的集合 public abstract Set selectedKeys( );//返回已选择的键的集合 }
内容转自http://blog.csdn.net/rogerjava/article/details/7343951
下面给出简单 c/s模式的例子
服务器端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class Server {
private Selector selector;
private ByteBuffer readBuffer = ByteBuffer.allocate(1024);//调整缓存的大小可以看到打印输出的变化
private ByteBuffer sendBuffer = ByteBuffer.allocate(1024);//调整缓存的大小可以看到打印输出的变化
String str;
public void start() throws IOException {
// 打开服务器套接字通道
ServerSocketChannel ssc = ServerSocketChannel.open();
// 服务器配置为非阻塞
ssc.configureBlocking(false);
// 进行服务的绑定
ssc.bind(new InetSocketAddress("localhost", 8001));
// 通过open()方法找到Selector
selector = Selector.open();
// 注册到selector,等待连接
ssc.register(selector, SelectionKey.OP_ACCEPT);
while (!Thread.currentThread().isInterrupted()) {
selector.select();
Set keys = selector.selectedKeys();
Iterator 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);
} else if (key.isWritable()) {
write(key);
}
keyIterator.remove(); //该事件已经处理,可以丢弃
}
}
}
private void write(SelectionKey key) throws IOException, ClosedChannelException {
SocketChannel channel = (SocketChannel) key.channel();
System.out.println("write:"+str);
sendBuffer.clear();
sendBuffer.put(str.getBytes());
sendBuffer.flip();
channel.write(sendBuffer);
channel.register(selector, SelectionKey.OP_READ);
}
private void read(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
// Clear out our read buffer so it's ready for new data
this.readBuffer.clear();
// readBuffer.flip();
// Attempt to read off the channel
int numRead;
try {
numRead = socketChannel.read(this.readBuffer);
} catch (IOException e) {
// The remote forcibly closed the connection, cancel
// the selection key and close the channel.
key.cancel();
socketChannel.close();
return;
}
str = new String(readBuffer.array(), 0, numRead);
System.out.println(str);
socketChannel.register(selector, SelectionKey.OP_WRITE);
}
private void accept(SelectionKey key) throws IOException {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = ssc.accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
System.out.println("a new client connected "+clientChannel.getRemoteAddress());
}
public static void main(String[] args) throws IOException {
System.out.println("server started...");
new Server().start();
}
}
客户端
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;
public class Client {
ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
public void start() throws IOException {
// 打开socket通道
SocketChannel sc = SocketChannel.open();
//设置为非阻塞
sc.configureBlocking(false);
//连接服务器地址和端口
sc.connect(new InetSocketAddress("localhost", 8001));
//打开选择器
Selector selector = Selector.open();
//注册连接服务器socket的动作
sc.register(selector, SelectionKey.OP_CONNECT);
Scanner scanner = new Scanner(System.in);
while (true) {
//选择一组键,其相应的通道已为 I/O 操作准备就绪。
//此方法执行处于阻塞模式的选择操作。
selector.select();
//返回此选择器的已选择键集。
Set keys = selector.selectedKeys();
System.out.println("keys=" + keys.size());
Iterator keyIterator = keys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
keyIterator.remove();
// 判断此通道上是否正在进行连接操作。
if (key.isConnectable()) {
sc.finishConnect();
sc.register(selector, SelectionKey.OP_WRITE);
System.out.println("server connected...");
break;
} else if (key.isWritable()) { //写数据
System.out.print("please input message:");
String message = scanner.nextLine();
//ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes());
writeBuffer.clear();
writeBuffer.put(message.getBytes());
//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
writeBuffer.flip();
sc.write(writeBuffer);
//注册写操作,每个chanel只能注册一个操作,最后注册的一个生效
//如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来
//int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
//使用interest集合
sc.register(selector, SelectionKey.OP_READ);
sc.register(selector, SelectionKey.OP_WRITE);
sc.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()){//读取数据
System.out.print("receive message:");
SocketChannel client = (SocketChannel) key.channel();
//将缓冲区清空以备下次读取
readBuffer.clear();
int num = client.read(readBuffer);
System.out.println(new String(readBuffer.array(),0, num));
//注册读操作,下一次读取
sc.register(selector, SelectionKey.OP_WRITE);
}
}
}
}
public static void main(String[] args) throws IOException {
new Client().start();
}
}