目录
为什么要用到NIO?
适用于连接数目比较小且固定的场景,这种方式对服务器资源要求比较高,并发局限于应用中。
NIO
1.用于线程来轮询监控多个数据传输通道,哪个甬道准备好了(即有了一组可以处理的数据),就处理哪个通道
2.面向缓冲区的,非阻塞式的,基于选择器的
核心API
1.Channel 通道
2.Buffer 缓冲区
3.Selector 选择器
通道和流的区别
1.既可以从通道中读取数,又可以写数据到通道.但流的读写通常是单向的。
2.通道可以异步地读取。
3.通道中的数据总是先读到一个Buffer,或者总是要从一个Buffer中写入
通道的重要实现类
1.FileChannel 从文件中读取数据
2.DatagramChannel 通过UDP读写网络中的数据
3. Sockect 能通过TCP读写网络中的数据
4.ServerSocketChannel 可以监听新进来的TCP连接
package IO.nio;
import java.nio.IntBuffer;
public class BufferDemo {
private static IntBuffer intBuffer;
public static void main(String[] args) {
//1.创建buffer
intBuffer = IntBuffer.allocate(5);
//2.向buffer传入数据
for(int i=0 ; i<5 ;i++){
intBuffer.put(i*2);
}
//3.读写切换
intBuffer.flip();
//4.buffer的读取
while(intBuffer.hasRemaining()){
System.out.println(intBuffer.get());
}
}
}
package IO.nio;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ChannelDemo {
public static void main(String[] args) throws IOException {
String str = "hello";
FileOutputStream fileOutputStream = new FileOutputStream("Test.txt");
//文件数据的读写 ScoketChonnel 网络编程常用
FileChannel fileChannel = fileOutputStream.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
byteBuffer.put(str.getBytes());
byteBuffer.flip();
//写入数据
fileChannel.write(byteBuffer);
fileOutputStream.close();
}
}
Selector
检测多个注册上来的通道中是否有事件发生,好处就是避免多行线程的情况下上下文的切换,导致的额外开销.
案例:
服务器:
package IO.nio;
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.Set;
public class NIOServer {
public static void main(String[] args) throws IOException {
//1.创建ServerSocketChannel
ServerSocketChannel serverSocketChannel =ServerSocketChannel.open();
//2.创建select对象
Selector selector = Selector.open();
//3.绑定编口号 在服务器监听
serverSocketChannel.socket().bind(new InetSocketAddress(6666));
//4.非阻塞设置
serverSocketChannel.configureBlocking(false);
//5.绑定端口号,在服务器端监听
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while(true){
if(selector.select(1000) == 0){
System.out.println("服务器等待1秒,无连接");
}
Set<SelectionKey> selectionKeySet = selector.selectedKeys();
Iterator<SelectionKey>keyIterator = selectionKeySet.iterator();
while (keyIterator.hasNext()){
SelectionKey key = keyIterator.next();
//根据key对于的通道获取事件并作出处理
if(key.isAcceptable()){
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);//非阻塞设置
socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
}
if(key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
channel.read(byteBuffer);
System.out.println("from客户端"+new String(byteBuffer.array()));
}
//手动溢出当前SelectionKey,防止多线程情况下的重复操作
keyIterator.remove();
}
}
}
}
客户端:
package IO.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class NIOClient {
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
InetSocketAddress inetSocketAddress = new InetSocketAddress(6666);
if(!socketChannel.connect(inetSocketAddress)){
while(!socketChannel.finishConnect()){
System.out.println("等待连接");
}
}
String str = "hello";
ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
//发送数据
socketChannel.write(buffer);
System.in.read();
}
}
先开启服务器,接着开启客户端连接,就可以观察到: