NIO中比较重要的有:Buffer,ByteBuffer(要了解此类的postion、limit等)、chanel(SelectableChannel、ServerSocketChannel、SocketChanel)和Selector、SelectorKey,注意SelectorKey的种类ServerSocketChannel比SocketChanel多一个accept,其他三个为connect、read和write,因为ServerSocketChannel对应于旧IO中的ServerSocket,SocketChannel对应于旧IO中的Socket。DataProgramChannel与SocketChanel类似。
下面是一个Server和Client的例子,用来使用NIO进行通信。简单的说,就是创建好对象channel,接着在channel上注册好监听事件SelectorKey,然后调用channel的select()得到所有的Key的set,然后iterate该set根据具体的Key类型进行处理,基本上Key分为accept, read 和write,注意在指定方法处理key之前要将此key从interator中删除, accept就是server接受client的请求,read和write是读写byteBuffer。注意的是*Buffer有两个方法比较重要clear和flip,前者是清空,后者是紧实,因为*Buffer在创建的时候指定了大小,而每次读写实际上Buffer中的东东可能未满,就要通过flip来紧实一下喽,即在读Buffer中的数据之前都要进行一下flip, 如果这个buffer还要重用则就要进行clear。
import java.io.IOException;
import java.net.InetAddress;
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.nio.charset.Charset;
import java.util.Date;
import java.util.Iterator;
import java.util.Set;
public class Server{
private final Selector selector;
private final ServerSocketChannel serverSocketChannel;
public Server(int port) throws IOException{
// 创建选择器
selector = Selector.open();
// 打开监听信道
serverSocketChannel = ServerSocketChannel.open();
InetSocketAddress adress = new InetSocketAddress(InetAddress.getLocalHost(),port);
//与本地端口绑定
serverSocketChannel.socket().bind(adress);
// 设置为非阻塞模式
serverSocketChannel.configureBlocking(false);
// 注册选择器.并在注册过程中指出该信道可以进行Accept操作
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
}
public void start() {
System.out.println("the server is started......");
while (true) {
try {
int nKeys = selector.select();
if (nKeys > 0){
// selectedKeys()中包含了每个准备好某一I/O操作的信道的SelectionKey
Set<SelectionKey> scSet = selector.selectedKeys();
Iterator<SelectionKey> iter = scSet.iterator();
while (iter.hasNext()) {
SelectionKey key = (SelectionKey) iter.next();
iter.remove();
dispatch(key);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void dispatch(SelectionKey key) {
// 有客户端连接请求时
//if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
if (key.isAcceptable()) {
try {
System.out.println("Key is acceptable");
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel socket = (SocketChannel) ssc.accept();
socket.configureBlocking(false);
socket.register(selector, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
// 从客户端读取数据
//else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
else if (key.isReadable()) {
System.out.println("the key is readable");
// new Thread(new ReadeHandler(key)).start();
try {
SocketChannel socket = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = socket.read(buffer);
if (bytesRead > 0) {
buffer.flip();
// 将字节转化为为UTF-16的字符串
String receivedString = Charset.forName("UTF-16").newDecoder().decode(buffer).toString();
// 控制台打印出来
System.out.println("接收到来自"
+ socket.socket()
.getRemoteSocketAddress()
+ "的信息:" + receivedString);
// 准备发送的文本
String sendString = "你好,客户端. @"
+ new Date().toString() + ",已经收到你的信息:"
+ receivedString;
buffer = ByteBuffer.wrap(sendString
.getBytes("UTF-16"));
socket.write(buffer);
// 设置为下一次读取或是写入做准备
key.interestOps(SelectionKey.OP_READ);
}
} catch (IOException e) {
//客户端断开连接,所以从Selector中取消注册
key.cancel();
if(key.channel() != null)
try {
key.channel().close();
System.out.println("the client socket is closed!");
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
// 客户端可写时
else if (key.isWritable()) {
System.out.println("tHe key is writable");
//new Thread(new WriteHandler(key)).start();
//do something
}
}
public static void main(String[] args) throws IOException {
Server server = new Server(9911);
server.start();
}
}
import java.io.IOException;
import java.net.InetAddress;
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.nio.charset.Charset;
public class Client {
// 信道选择器
private Selector selector;
// 与服务器通信的信道
SocketChannel socketChannel;
public Client(int port)throws IOException{
selector = Selector.open();
socketChannel = SocketChannel.open(new InetSocketAddress(InetAddress.getLocalHost(),port));
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
// 启动读取线程
new Thread(new ClientReadThread()).start();
}
//发送字符串到服务器
public void sendMsg(String message) throws IOException{
ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes("UTF-16"));
socketChannel.write(writeBuffer);
}
public static void main(String[] args) throws IOException{
Client client = new Client(9911);
client.sendMsg("你好!Nio!");
}
class ClientReadThread implements Runnable{
public void run() {
try {
while (selector.select() > 0) {
// 遍历每个有可用IO操作Channel对应的SelectionKey
for (SelectionKey sk : selector.selectedKeys()) {
// 如果该SelectionKey对应的Channel中有可读的数据
if (sk.isReadable()) {
// 使用NIO读取Channel中的数据
SocketChannel sc = (SocketChannel) sk.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
sc.read(buffer);
buffer.flip();
// 将字节转化为为UTF-16的字符串
String receivedString = Charset.forName("UTF-16").newDecoder().decode(buffer).toString();
// 控制台打印出来
System.out.println("接收到来自服务器" + sc.socket().getRemoteSocketAddress() + "的信息:" + receivedString);
// 为下一次读取作准备
sk.interestOps(SelectionKey.OP_READ);
}
// 删除正在处理的SelectionKey
selector.selectedKeys().remove(sk);
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}