何为NIO,百度百科上的解释为:
java.nio全称java non-blocking IO(实际上是 new io),是指jdk1.4 及以上版本里提供的新api(New IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。
为所有的原始类型提供(Buffer)缓存支持。字符集编码解码解决方案。 Channel :一个新的原始I/O 抽象。 支持锁和内存映射文件的文件访问接口。 提供多路(non-blocking) 非阻塞式的高伸缩性网络I/O 。
简单来说,传统Java是面向对象的,而NIO是面向缓冲区的,传统的IO是阻塞的,NIO是非阻塞的。
通道(channel)和流(stream)最大的不同,就是流是单向的,通道是双向的。
NIO中的关键Buffer实现有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer,分别对应基本数据类型: byte, char, double, float, int, long, short。当然NIO中还有MappedByteBuffer, HeapByteBuffer, DirectByteBuffer等
Selector运行单线程处理多个Channel,如果你的应用打开了多个通道,但每个连接的流量都很低,使用Selector()方法就会很方便,例如在一个聊天服务器中。要使用Selector, 得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新的连接进来、数据接收等。
首先
向buffer中写数据用fileChannel.read()
向buffer中读数据用fileChannel.write()
read返回值为-1的时候表示连接断开
客户端代码用NIO写:
package NIO;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.concurrent.TimeUnit;
import java.util.logging.SocketHandler;
public class Client {
public static void main(String[] args){
client();
}
public static void client(){
ByteBuffer buffer = ByteBuffer.allocate(1024);
SocketChannel socketChannel = null;
try{
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("127.0.0.1",9999));
if(socketChannel.finishConnect()){
int i = 0;
while(true){
TimeUnit.SECONDS.sleep(1);
String info = "I'm" + i++ + "-th information from client";
buffer.clear();
buffer.put(info.getBytes());
buffer.flip();//flip方法将Buffer从写模式切换到读模式。调用flip()方法会将position设回0,并将limit设置成之前position的值。
while(buffer.hasRemaining()){
System.out.println(buffer);
socketChannel.write(buffer);
}
}
}
}catch (IOException | InterruptedException e){
e.printStackTrace();
}
finally {
try{
if(socketChannel != null){
socketChannel.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
}
服务端代码先用传统IO来写:
package NIO;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.stream.IntStream;
public class Server {
public static void main(String[] arg){
server();
}
public static void server(){
ServerSocket serverSocket = null;
InputStream in = null;
try{
serverSocket = new ServerSocket(9999);
int recvMsgSize = 0;
byte[] recvBuf = new byte[1024];
while (true){
Socket clntSocket = serverSocket.accept();
SocketAddress clientAddress = clntSocket.getRemoteSocketAddress();//获取另一端的IP和端口
System.out.println("Handing client at"+clientAddress);
in = clntSocket.getInputStream();
while((recvMsgSize = in.read(recvBuf)) != -1){
byte[] temp = new byte[recvMsgSize];
System.arraycopy(recvBuf,0,temp,0,recvMsgSize);
System.out.println(new String(temp));
}
}
}catch (IOException e){
e.printStackTrace();
}
finally {
try{
if(serverSocket != null){
serverSocket.close();
}
if(in != null){
in.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
}
用NIO写的服务端:
package NIO;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
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 ServerConnect {
private static final int BUF_SIZE = 1024;
private static final int PORT = 9999;
private static final int TIMEOUT = 3000;
public static void main(String[] args){
selector();
}
public static void handleAccept(SelectionKey key) throws IOException {
ServerSocketChannel ssChannel = (ServerSocketChannel)key.channel();
SocketChannel sc = ssChannel.accept();
sc.configureBlocking(false);
sc.register(key.selector(),SelectionKey.OP_READ, ByteBuffer.allocateDirect(BUF_SIZE));//
用selector必须要注册
}
public static void handleRead(SelectionKey key)throws IOException{
SocketChannel sc = (SocketChannel)key.channel();
ByteBuffer buf = (ByteBuffer)key.attachment();
long bytesRead = sc.read(buf);
while(bytesRead > 0){
buf.flip();
while(buf.hasRemaining()){
System.out.println((char)buf.get());
}
System.out.println();
buf.clear();
bytesRead = sc.read(buf);
}
if(bytesRead == -1){
sc.close();
}
}
public static void handleWrite(SelectionKey key)throws IOException{
ByteBuffer buf = (ByteBuffer)key.attachment();
buf.flip();
SocketChannel sc = (SocketChannel)key.channel();
while(buf.hasRemaining()){
sc.write(buf);
}
buf.compact();//将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面
}
public static void selector(){
Selector selector = null;
ServerSocketChannel ssc = null;
try{
selector = Selector.open();
ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(PORT));
ssc.configureBlocking(false);
ssc.register(selector,SelectionKey.OP_ACCEPT);
while(true){
if(selector.select(TIMEOUT) == 0){
System.out.println("==");
continue;
}
Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
while(iter.hasNext()){
SelectionKey key = iter.next();
if(key.isAcceptable()){
handleAccept(key);
}
if(key.isReadable()){
handleRead(key);
}
if(key.isWritable() && key.isValid()){
handleWrite(key);
}
if(key.isConnectable()){
System.out.println("isConnectable = true");
}
iter.remove();
}
}
}catch (IOException e){
e.printStackTrace();
}finally {
try{
if(selector != null){
selector.close();
}
if(ssc != null){
ssc.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
}