NIO核心组件:
缓冲区Buffer:包在对象内的基本数据元素数组、固定数量的数据的容器
属性:容量Capacity、上界limit、位置position、标记mark
操作:存put、取get
访问io中的数据需要通过Buffer进行操作
通道:Channel
网络数据通过channel读取、写入
多路复用器Selector:
NIO编程基础,提供了选择已经就绪的任务的能力,轮询注册在其上的Channel,某个通道发生读、写事件,该通道便处于就绪状态,被selector轮询出来,后通过 SelectionKey 可以获取就绪 Channel 的集合,进行后续的 I/O 操作。
阻塞与非阻塞
进程访问数据时,数据是否准备就绪的一种处理方式
没有准备时:
阻塞:等待缓存区中的数据准备好才处理其他事情
非阻塞:进程访问、未准备好,返回不等,有数据也返回
同步于异步:基于应用程序和操作系统处理IO时间锁
同步:直接参与IO读写操作,阻塞在某个方法上等待IO时间完成
对线程的性能
IO事件的轮询:多路复用技术(select模式)
读写事件交给单独线程处理,完成IO事件的注册功能、同时不断去轮询读写缓冲区(是否有数据准备好)通知相应读写线程,之前的读写线程可以做其他的事情,但是阻塞select线程
client
select管家
Boss
客人来 ,对管家说:我来了
管家得到这个注册信息后 对boss说:我这里有一个、多个客人 boss你去给A这件东西、给B哪个东西(此时客人可以去做自己的事情)
管家知道boss给他任务后,就会根据注册信息 找对应的客人, 告诉他boss给他*东西
异步:
所有IO读写交给操作系统去处理,处理完给应用程序一个通知
JavaIO模式
BIO:性能开销,java1.4之前
NIO:当前主流 jdk1.4 多路复用技术 select模式 实现IO事件轮询
同步非阻塞的模式
AIO:jdk1.7 NIO2 真正的异步
NIO AIO原理
原理基础上serversocket、socket做一个改进
Socket 三次握手(连接性能开销大:减少连接次数) serversocket
读和写采用抽象的管道:一个TCP连接间抽象、一个TCP可以对应多个管道:减少TCP连接次数
代码:
service:连接
public class NioServiceTest {
private int blockSize = 4096;
private ByteBuffer sendBuffer = ByteBuffer.allocate(blockSize);
private ByteBuffer receivebuffer = ByteBuffer.allocate(blockSize);
private Selector selector;
private int port = 8080;
public NioServiceTest(int port) throws IOException {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//非阻塞
serverSocketChannel.configureBlocking(false);
// 检索与此通道关联的服务器套接字
ServerSocket serverSocket = serverSocketChannel.socket();
//绑定ip和端口
serverSocket.bind(new InetSocketAddress(this.port));
//打开选择器,找到Selector
selector = Selector.open();
// 注册到selector,等待连接
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
//监听
public void listen() throws IOException {
while (true){
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> itetor = selectionKeys.iterator();
while (itetor.hasNext()){
SelectionKey selectionKey = itetor.next();
itetor.remove();
handleKey(selectionKey);
}
}
}
public void handleKey(SelectionKey selectionKey) throws IOException {
ServerSocketChannel server = null;// 接受请求
SocketChannel client = null;
String reciveText ;
String sendText;
int cout =0;
int flag =0;
// 测试此键的通道是否已准备好接受新的套接字连接。
if(selectionKey.isAcceptable()){
// 返回为之创建此键的通道。
server= (ServerSocketChannel) selectionKey.channel();
client = server.accept(); // 接受到此通道套接字的连接。
client.configureBlocking(false);
client.register(selector,selectionKey.OP_READ);
}else if(selectionKey.isReadable()){
client = (SocketChannel) selectionKey.channel();//获得客户端的channel
int count = client.read(receivebuffer);//读取服务器发送来的数据到缓冲区中
if(count>0){
reciveText = new String(receivebuffer.array(),0,count);
System.out.println("服务端接收到客户端的信息"+reciveText);
}
}else if(selectionKey.isWritable()){
sendBuffer.clear();//写事件,写之前先清空以备下次读取
client = (SocketChannel) selectionKey.channel();
//发送的数据
sendText = "msg send to client ;" + flag++;
sendBuffer.put(sendText.getBytes());//写到缓存区
sendBuffer.flip();//将缓冲区各标志复位,因为向里面put了数据,标志被改变,要想从中读取数据发向服务器,就要复位
client.write(sendBuffer);
System.out.println("服务器端发送数据给客户端"+sendText);
}
}
public static void main(String[] args) throws IOException {
int port = 7080;
NioServiceTest server = new NioServiceTest(port);
server.listen();
}
}
client:
public class NioClientTest {
private static int flag;
private static int blockSize = 4096;
private static ByteBuffer sendBuffer = ByteBuffer.allocate(blockSize);
private static ByteBuffer receivebuffer = ByteBuffer.allocate(blockSize);
private final static InetSocketAddress serverAddress = new InetSocketAddress("127.0.0.1", 7080);
public static void main(String[] args) throws IOException {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
//打开选择器
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
socketChannel.connect(serverAddress);//连接
Set<SelectionKey> selectionKeys;
Iterator<SelectionKey> iterator;
SelectionKey selectionKey;
SocketChannel client;
String receiveText;
String sendText;
while (true) {
selectionKeys = selector.selectedKeys();
iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
selectionKey = iterator.next();
if (selectionKey.isConnectable()) {
client = (SocketChannel) selectionKey.channel();
if (client.isConnectionPending()) {
client.finishConnect();
System.out.println("客户端完成连接操作");
sendBuffer.clear();
sendBuffer.put("hello service".getBytes());
sendBuffer.flip();
client.write(sendBuffer);
}
client.register(selector, SelectionKey.OP_READ);
}
if (selectionKey.isReadable()) {
client = (SocketChannel) selectionKey.channel();
receivebuffer.clear();
int count = client.read(receivebuffer);
if (count > 0) {
receiveText = new String(receivebuffer.array(), 0, count);
System.out.println("客户端接收到服务端的数据" + receiveText);
client.register(selector, SelectionKey.OP_WRITE);
}
}
if (selectionKey.isWritable()) {
sendBuffer.clear();
client = (SocketChannel) selectionKey.channel();
sendText = "msg send to server" + flag++;
sendBuffer.put(sendText.getBytes());
sendBuffer.flip();
client.write(sendBuffer);
System.out.println("客户端发送数据给服务端"+sendText);
client.register(selector,SelectionKey.OP_READ);
}
}
selectionKeys.clear();
}
}
}
---------------------
作者:星小丫头辰
来源:CSDN
原文:https://blog.csdn.net/ma15732625261/article/details/80157185
版权声明:本文为博主原创文章,转载请附上博文链接!