NIO也就是非堵塞的IO,通过Selector来注册事件,用死循环来不断轮询被注册的通道,一旦有某个通道符合条件,就会被挑选出来到selectedKeys中进行处理,处理完毕后要把selectedKeys清空,进入到下次轮询。
NIO十分高效,节省服务器的资源
下面是NIO的服务器端
package nio;
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;
public class MyServer {
public static void main(String[] args) throws Exception {
ByteBuffer buffer = ByteBuffer.allocate(1024);
* 开启挑选器
Selector sel = Selector.open();
* 开启ServerSocket通道
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress("localhost", 8888));
* 设置非堵塞模式,默认是堵塞
ssc.configureBlocking(false);
* 在挑选器中注册通道(服务器通道)
ssc.register(sel, SelectionKey.OP_ACCEPT);
while(true) {
* 该方法是堵塞的,只有选择到key才会向下执行
sel.select();
Iterator<SelectionKey> it = sel.selectedKeys().iterator();
while(it.hasNext()) {
SelectionKey key = it.next();
* 有通道,接收并注册
if(key.isAcceptable()) {
System.out.println("当前的的selector:" + key + ", 有连接进来了");
ServerSocketChannel sc = (ServerSocketChannel) key.channel();
SocketChannel sc0 = sc.accept();
* 设置SocketChannel为非堵塞
sc0.configureBlocking(false);
* 注册读事件
sc0.register(sel, SelectionKey.OP_READ);
}
if(key.isReadable()) {
System.out.println("当前的的selector:" + key + ", 读取数据事件");
SocketChannel sc1 = (SocketChannel) key.channel();
* 回送hello:
byte[] helloByte = "hello:".getBytes();
buffer.put(helloByte, 0, helloByte.length);
while(sc1.read(buffer) != 0) {
buffer.flip();
sc1.write(buffer);
buffer.clear();
}
}
sel.selectedKeys().clear();
}
}
}
}
客户端(使用一般的socket处理数据也是可以的)
package nio;
import java.io.ByteArrayOutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
public class MyClient {
public static void main(String[] args) throws Exception {
Selector sel = Selector.open();
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("localhost", 8888));
sc.configureBlocking(false);
sc.register(sel, SelectionKey.OP_READ);
ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);
buffer.put("everything will be good".getBytes());
buffer.flip();
sc.write(buffer);
buffer.clear();
while(true) {
sel.select();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while(sc.read(buffer) > 0) {
buffer.flip();
baos.write(buffer.array(), 0, buffer.limit());
buffer.clear();
}
baos.close();
System.out.println(new String(baos.toByteArray()));
sel.selectedKeys().clear();
}
}
}
文件通道ByteBuffer
Buffer
1.mark,记号,(reset)
2.position,当前指针位置索引值
3.limit,限制,可以使用的数组长度,从开始计算
4.capactiy,容量,是数组的长度。
约束条件
0 <= mark <= pos <= limit <= capacity
ByteBuffer
flip() 拍板,
package TestCase;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import org.junit.Test;
public class TestFileChannel {
* 利用文件通道来复制文件
@Test
public void test1() throws Exception {
* 输入
FileInputStream fis = new FileInputStream("E://TestCase//day24//demo.jpg");
* 获得文件通道
FileChannel fcIn = fis.getChannel();
FileOutputStream fos = new FileOutputStream("E://TestCase//day24//demo2.jpg");
FileChannel fcOut = fos.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024*8);
while(fcIn.read(buffer) != -1) {
buffer.flip();
fcOut.write(buffer);
buffer.clear();
}
fcIn.close();
fcOut.close();
}
* 将文件读取到内存中
@Test
public void test2() throws Exception {
File f = new File("E://TestCase//day24//demo.jpg");
RandomAccessFile raf = new RandomAccessFile(f, "rw");
MappedByteBuffer buffer = raf.getChannel().map(MapMode.READ_ONLY, 1, 100);
System.out.println(buffer.get(0));
System.out.println(buffer.capacity());
raf.close();
}
* 测试离堆内存
@Test
public void test3() throws Exception{
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
Class clazz = Class.forName("java.nio.DirectByteBuffer");
Field f = clazz.getDeclaredField("cleaner");
f.setAccessible(true);
Object cleaner = f.get(buffer);
System.out.println(cleaner);
Class clazz2 = Class.forName("java.lang.ref.Cleaner");
Method m = clazz2.getDeclaredMethod("clean", null);
m.invoke(cleaner, null);
System.out.println("end");
}
}