看了挺多次有关于NIO的文章,但是由于项目中用到的少所以不久就忘了,所以在此做个笔记。里面很多内容都是参考的别人的文章还有书籍,希望可以帮到一两个人吧
1、缓冲区Buffer
buffer是一个对象,里面包括了要写入或者读取的数据,面向流的i/o流中所有数据的读写都是直接读或者写到Stream对象中,而NIO是写道buffer中的。所以i/o是面向流的而NIO是面向缓冲区的。
bugger的底层是一个数组,但是其中包含了四种属性
1、capacity
容量表示缓冲区的大小,其实就是缓冲区域内部数组的大小
2、position
位置表示缓冲区中当前可读写的位置,当调用get(),put()方法时会自动更新position位置
3、limit
表示缓冲区中可用元素的大小。当发生读/写转换时会发生改变,当写缓冲区时,limit等于capacity,表示整个缓冲区都可以写,当读缓冲区时,limit代表缓冲区中已有元素的大小,表示读取缓冲区的位置不能超过limit。
4、mark
mark是备忘位置,可以暂时存储position,以便position改变后可以回到原来位置。
最常用的缓冲区是ByteBuffer,每一种java基本类型(除了Boolean)都对应一种缓冲区。
2、通道Channel
Channel是一个通道,是一个向自来水管一样的*双向*通道,流是单向的只能读或者写,而通道可以读写同时进行。
3、多路复用器Selector
熟练地使用Selector对于NIO十分重要,多路复用器提供已经就绪的任务的能力,简单来说,Selector会不断的轮询注册在其上的Channel,如果某个Channel上面发生读或者写事件,这个Channel就会处于就绪状态,会被Selector轮询出来,然后通过SelectorKey可以获取就绪的Channe集合,然后进行后续的I/O操作。
一个多路复用器可以同时轮询多个Channel,犹豫JDK使用了epoll()代替传统的select实现,所以它没有最大连接句柄1024/2048的限制,这也就意味着只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端。
package com.dnz.nio;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
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;
/**
* @author zzz
* @date 2020/1/3 11:16
*/
public class NioServer {
private Selector selector;
public void initServer(int port) throws IOException {
//获得一个ServerSocketChannel通道
ServerSocketChannel channel = ServerSocketChannel.open();
//设置通道为非阻塞
channel.configureBlocking(false);
//将该通道对应的ServerSocket绑定到对应port
channel.socket().bind(new InetSocketAddress(port));
//获得通道管理器
this.selector = Selector.open();
//绑定通道和通道管理器,并为该通道注册为SelectionKey.OP_ACCEPT事件
///当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞
channel.register(this.selector,SelectionKey.OP_ACCEPT);
}
/**
* 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
* @throws IOException
*/
@SuppressWarnings("unchecked")
public void listen() throws IOException {
System.out.println("服务端启动成功!");
// 轮询访问selector
while (true){
//当注册的事件到达时,方法返回;否则,该方法会一直阻塞
selector.select();
// 获得selector中选中的项的迭代器,选中的项为注册的事件
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
// 删除已选的key,以防重复处理
iterator.remove();
// 客户端请求连接事件
if (key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel) key.channel();
// 获得和客户端连接的通道
SocketChannel channel = server.accept();
// 设置成非阻塞
channel.configureBlocking(false);
//这里编辑给客户端发送的信息
channel.write(ByteBuffer.wrap(new String("可以向我发送信息了").getBytes()));
//在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
channel.register(this.selector, SelectionKey.OP_READ);
//获取可读的事件
}else if (key.isReadable()){
read(key);
}
}
}
}
/**
* 处理读取客户端发来的信息和事件
* */
public void read(SelectionKey key) throws IOException{
//服务器可读取消息:得到事件发生的socket通道
SocketChannel channel = (SocketChannel) key.channel();
//创建读取的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(125);
int read = channel.read(buffer);
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("来自客户端的信息"+msg);
ByteBuffer buffer1 = ByteBuffer.wrap(msg.getBytes());
channel.write(buffer1);
}
//启动服务端测试
public static void main(String[] args) throws IOException {
NioServer server = new NioServer();
server.initServer(8000);
server.listen();
}
}
package com.dnz.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.SocketChannel;
import java.util.Iterator;
/**
* @author zzz
* @date 2020/1/3 11:16
*/
public class NioClient {
private Selector selector;
/***
* 获得一个Socket通道,并对该通道做一些初始化的工作
*/
public void initClient(String ip,int port) throws IOException {
//获得一个socket通道
SocketChannel channel = SocketChannel.open();
//设置非阻塞
channel.configureBlocking(false);
//获得通道管理器
this.selector = Selector.open();
// 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
//用channel.finishConnect();才能完成连接
channel.connect(new InetSocketAddress(ip,port));
//将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
channel.register(selector,SelectionKey.OP_CONNECT);
}
/***
* 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
*/
public void listen() throws IOException {
//轮询访问selector
while (true){
//当注册的事件到达时,方法返回;否则,该方法会一直阻塞
selector.select();
//获取selector中选中项的迭代器
Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
// 删除已选的key,以防重复处理
iterator.remove();
//连接事件发生
if (key.isConnectable()){
SocketChannel channel = (SocketChannel) key.channel();
//如果正在连接,则完成连接
if (channel.isConnectionPending()){
channel.finishConnect();
}
//设置成非阻塞
channel.configureBlocking(false);
//在这里可以给服务端发送信息哦
channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes()));
//在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
channel.register(this.selector,SelectionKey.OP_READ);
}else if (key.isReadable()){
read(key);
}
}
}
}
public void read(SelectionKey key) throws IOException{
//服务器可读取消息:得到事件发生的socket通道
SocketChannel channel = (SocketChannel) key.channel();
//创建读取的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(125);
int read = channel.read(buffer);
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("来自客户端的信息"+msg);
ByteBuffer buffer1 = ByteBuffer.wrap(msg.getBytes());
channel.write(buffer1);
}
//启动客户端测试
public static void main(String[] args) throws IOException {
NioClient client = new NioClient();
client.initClient("localhost",8000);
client.listen();
}
}
转载自:https://www.cnblogs.com/zedosu/p/6666984.html