文章目录
Java NIO基本介绍
package com.zj.nio;
import java.nio.IntBuffer;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/6/3 0003 14:20
* 说明Buffer的使用
*/
public class BasicBuffer {
public static void main(String[] args) {
//距离说明Buffer的舒勇
//创建一个Buffer,大小为5
IntBuffer intBuffer = IntBuffer.allocate(5);
//向Buffer存放数据
// intBuffer.put(10);
for(int i=0;i<intBuffer.capacity();i++){
intBuffer.put(i*2);
}
//如何从buffer获取数据
//将buffer转换,读写切换
/*
limit = position;
position = 0;
mark = -1;
return this;
*/
intBuffer.flip();
while (intBuffer.hasRemaining()){
System.out.println(intBuffer.get());
}
}
}
nio与bio的区别
NIO三大核心原理示意图
缓冲区
基本介绍
Buffer类及其子类
- 在NIO中,Buffer是一个顶层父类,它是一个抽象类,类的层级关系图:
- Buffer类定义了所有的缓冲区都具有的四个属性来提供关于其所包含的数据元素的信息:
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
//位置,下一个要被读或写的元素的索引
//每次读写缓冲区数据时东辉改变该值,为下次读写作准备
private int position = 0;
//表示缓冲区的当前终点,不能对缓冲区超过极限的位置进行读写操作,且极限是可以修改的
private int limit;
//容量,既可以容纳的最大数据量,在缓冲区创建时被设定并且不能改变
private int capacity;
- Buffer类相关方法一览
一个重要的方法,反转
缓冲区,flip(),切换读写模式
/**
* Flips this buffer. The limit is set to the current position and then
* the position is set to zero. If the mark is defined then it is
* discarded.
*
* <p> After a sequence of channel-read or <i>put</i> operations, invoke
* this method to prepare for a sequence of channel-write or relative
* <i>get</i> operations. For example:
*
* <blockquote><pre>
* buf.put(magic); // Prepend header
* in.read(buf); // Read data into rest of buffer
* buf.flip(); // Flip buffer
* out.write(buf); // Write header + data to channel</pre></blockquote>
*
* <p> This method is often used in conjunction with the {@link
* java.nio.ByteBuffer#compact compact} method when transferring data from
* one place to another. </p>
*
* @return This buffer
*/
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
ByteBuffer
从前面可以看出对于Java中的基本数据类型(boolean除外),都有一个Buffer类型与之向对应,最常用的自然是
ByteBuffer
(二进制数据),该类的主要方法如下:
通道(Channel)
基本介绍
FileChannel类
FIleChannel主要用来对本地文件进行IO操作,常见的方法有:
/**
* Reads a sequence of bytes from this channel into the given buffers.
*
* <p> Bytes are read starting at this channel's current file position, and
* then the file position is updated with the number of bytes actually
* read. Otherwise this method behaves exactly as specified in the {@link
* ScatteringByteChannel} interface. </p>
* 将数据从通道读到缓冲区中
*/
public final long read(ByteBuffer[] dsts) throws IOException {
return read(dsts, 0, dsts.length);
}
/**
* Writes a sequence of bytes to this channel from the given buffer.
*
* <p> Bytes are written starting at this channel's current file position
* unless the channel is in append mode, in which case the position is
* first advanced to the end of the file. The file is grown, if necessary,
* to accommodate the written bytes, and then the file position is updated
* with the number of bytes actually written. Otherwise this method
* behaves exactly as specified by the {@link WritableByteChannel}
* interface. </p>
* 将数据从缓冲区写入到通道中
*/
public abstract int write(ByteBuffer src) throws IOException;
本地文件写数据
package com.zj.nio.channel;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/6/3 0003 14:59
*/
public class NIOFileChannel {
public static void main(String[] args) throws IOException {
String str = "Hello,尚硅谷";
//创建一个输出流----》channel
FileOutputStream fileOutputStream = new FileOutputStream("G:\\file01.txt");
//通过输出流获取对应文件Channel
/**
* 流--------> 文件
* |
*
* 将流包装成Chaneell
* 这个fileChannel真实类型是FileChannelImpl
* outputStream中包裹了FileChannel
*/
FileChannel fileChannel = fileOutputStream.getChannel();
//创建一个缓冲区 ByteBuffer
ByteBuffer byteBuffer =ByteBuffer.allocate(1024);
//将str放入bytebuffer
byteBuffer.put(str.getBytes());
//对bytebuffer进行flip(),从bytebuffer中进行postiotn
byteBuffer.flip();
//将bytebuffer数据写入到filechannel
//将数据写入到fileChannel
//向channelne内写
fileChannel.write(byteBuffer);
//关闭流
fileOutputStream.close();
}
}
本地文件读数据
package com.zj.nio.channel;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/6/3 0003 15:12
* 从文建中取数据
*/
public class NIOFILE02 {
public static void main(String[] args) throws IOException {
//创建一个输出流----》channel
FileInputStream fileInputStream = new FileInputStream("G:\\file01.txt");
//通过fileInputStream获取对应的FileChannel---》实际类型FileChannelImpl
FileChannel fileChannel = fileInputStream.getChannel();
//创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//将channel通道的数据读到buffer中
fileChannel.read(buffer);
//将缓冲区中的字节转换成字符串
System.out.println(new String(buffer.array()));
//关闭流
}
}
文件拷贝
package com.zj.nio.channel;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/6/3 0003 15:21
*
* 使用文件进行拷贝
*/
public class NIOFileChannel03 {
public static void main(String[] args) throws IOException {
//获取读入文件
FileInputStream fileInputStream = new FileInputStream("G:\\file01.txt");
FileOutputStream fileOutputStream = new FileOutputStream("G:\\file02.txt");
//获取Channel
FileChannel fileInputStreamChannel = fileInputStream.getChannel();
FileChannel fileOutputStreamChannel = fileOutputStream.getChannel();
//分配缓冲切
ByteBuffer buffer = ByteBuffer.allocate(1024);
//从输入文件中读入数据,写入到输出文件中
int len = -1;
//从输入文件读取到buffetr中
while((len=fileInputStreamChannel.read(buffer))!=-1){
//切换将buffe中的数据写入到输出文件中
//这就是为什么BUFFER中既能读数据也能写数据的关键
buffer.flip();
//将buffer中的数据写入到输出通道
fileOutputStreamChannel.write(buffer);
//清空buffer将标志为重置
buffer.clear();
}
}
}
使用transferFrom进行拷贝
package com.zj.nio.channel;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/6/3 0003 15:42
* 拷贝文件适用
*/
public class NIOFileChannel04 {
public static void main(String[] args) throws IOException {
//创建相关流
FileInputStream fileInputStream = new FileInputStream("G:\\01.png");
FileOutputStream fileOutputStream = new FileOutputStream("G:\\02.png");
//获取各个流对应的fileCahnnel
FileChannel fileInputStreamChannel = fileInputStream.getChannel();
FileChannel fileOutputStreamChannel = fileOutputStream.getChannel();
//使用transferFrom完成拷贝
fileOutputStreamChannel.transferFrom(fileInputStreamChannel,0,fileInputStreamChannel.size());
//关闭相关流
fileInputStream.close();
fileOutputStream.close();
}
}
Buffer和Channel的注意事项
Selector(选择器)
基本介绍
Selector示意图和特点说明
select的相关方法
示例:网络通信
服务器端
package com.zj.nio.DemoServerBase;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/6/4 0004 12:28
*/
public class NIOServer {
public static void main(String[] args) throws IOException {
//创建ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//得到一个Selector对象
Selector selector = Selector.open();
//绑定一个端口6666在服务器端监听
serverSocketChannel.socket().bind (new InetSocketAddress(6666));
//设置为非阻塞
serverSocketChannel.configureBlocking(false);
//把我们的ServerSocektChannel注册到Selector,关心事件为op_ACCP联结事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//循环等待客户端联结
while (true){
//这里我们等待1秒,这里若没有事件发生,就继续
if(selector.select(1000)==0){//没有事件发生
System.out.println("服务器等待了一秒,无连接");
continue;
}
//如果返回的不是0,;就获取到相关的SelectionKey集合
//1、如果返回大于0,表示已经获取到关注事件
//2、selector、selectedkeys()返回关注时间的集合
//通过selectionkeys反向获取通道
Set<SelectionKey> selectionKeys = selector.selectedKeys();
//遍历set<SelectionKey> 使用迭代器遍历
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()){
//获取selectionKey
SelectionKey key = keyIterator.next();
//根据key对应的通道发生的事件左响应的处理
if(key.isAcceptable()){//有新的客户端连接
//给该客户单生成一个SocketrChannel(这里不会阻塞,基于事件驱动)
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("客户单端生成了一个ScocketChannel"+socketChannel.hashCode());
//
socketChannel.configureBlocking(false);
//根据nio模型,将socekyChannel注册到selector说那个,关注时间为OP_READ
//同时给SocktChannel关联一个Buffer
socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
}
//发生
if(key.isReadable()){
//通过Key反向获取通道Channel
SocketChannel channel = (SocketChannel)key.channel();
//获取到该Channel关联的buffer
ByteBuffer buffer = (ByteBuffer)key.attachment();
//把当前通道数据读到buffer中
channel.read(buffer);
System.out.println("from客户单"+new String(buffer.array()));
}
//手动从集合中删除
keyIterator.remove();
}
}
}
}
客户端
package com.zj.nio.DemoServerBase;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/6/4 0004 12:46
*/
public class NIOClient {
public static void main(String[] args) throws IOException {
//得到一个网络通道
SocketChannel socketChannel = SocketChannel.open();
//设置非阻塞模式
socketChannel.configureBlocking(false);
//提供服务器端的ip和端口
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);
//连接服务器
if(!socketChannel.connect(inetSocketAddress)){
while (!socketChannel.finishConnect()){
System.out.println("因为连接需要时间,客户端不会阻塞,可以做其他工作");
}
//如果连接成功则可以发送数据
String str = "helloShangguighu";
ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
//发送数据,将buffer数据写入channel
socketChannel.write(buffer);
System.in.read();
}
}
}
SelectionKey
ServerSocketChannel
SocketChannel
聊天室
服务端
package com.zj.nio.DemoChatRoom;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/6/5 0005 10:55
*/
public class GroupChatServer {
//定义属性
private Selector selector;
private ServerSocketChannel listen;
private static final int PORT = 6667;
//构造器初始化
public GroupChatServer() {
try{
//得到选择器
selector = Selector.open();
//ServerSocektChanenel
listen = ServerSocketChannel.open();
//绑定端口
listen.socket().bind(new InetSocketAddress(PORT));
//设置为非阻塞模式
listen.configureBlocking(false);
//将该listen注册到selector
listen.register(selector, SelectionKey.OP_ACCEPT);
}catch (IOException e){
e.printStackTrace();
}
}
//监听
public void listen() {
try {
//循环处理
while (true) {
int count = selector.select(2000);
System.out.println("等待");
if (count > 0) {//有事件处理
//遍历得到的selectionkey集合
//如果返回的不是0,;就获取到相关的SelectionKey集合
//1、如果返回大于0,表示已经获取到关注事件
//2、selector、selectedkeys()返回关注时间的集合
//通过selectionkeys反向获取通道
Set<SelectionKey> selectionKeys = selector.selectedKeys();
//遍历set<SelectionKey> 使用迭代器遍历
Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
while (keyIterator.hasNext()) {
//获取selectionKey
SelectionKey key = keyIterator.next();
//根据key对应的通道发生的事件左响应的处理
if (key.isAcceptable()) {//有新的客户端连接
//给该客户单生成一个SocketrChannel(这里不会阻塞,基于事件驱动)
SocketChannel socketChannel = listen.accept();
socketChannel.configureBlocking(false);
//根据nio模型,将socekyChannel注册到selector说那个,关注时间为OP_READ
//同时给SocktChannel关联一个Buffer
socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
System.out.println(socketChannel.getLocalAddress()+"上线");
//
}
//发生可短 事件
if(key.isReadable()){
//专门写方法
readData(key);
}
//手动从集合中删除
keyIterator.remove();
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
//读取客户单消息
private void readData(SelectionKey key) throws IOException {
//定义一个SocketChannel
SocketChannel socketChannel = null;
try {
//获取到关联的channel
socketChannel = (SocketChannel)key.channel();
//穿件缓冲
ByteBuffer buffer = ByteBuffer.allocate(1024);
int count = socketChannel.read(buffer);
//根据count值处理
if(count>0){
//把缓冲区的数据转换成字符串
String msg = new String(buffer.array());
System.out.println("from客户daunt"+msg);
//向其他客户端发送消息(去掉自己),专门写一个发那个发处理
sendInfoTootherClients(msg,socketChannel);
}
}catch (Exception e){
//取消注册
key.cancel();
//关闭通道
socketChannel.close();
}
}
//转发消息给其他客户端(通道)
private void sendInfoTootherClients(String msg,SocketChannel selef){
System.out.println("服务器转发消息中");
//遍历所有注册到selector上的通道,并排除self
for(SelectionKey key:selector.keys()){
//通过key获取对应的SockeyChannel
Channel targetChannel = key.channel();
//排除自己
if(targetChannel instanceof SocketChannel && targetChannel!=selef){
//转型
SocketChannel dst = (SocketChannel)targetChannel;
//将msg存储到buffer
ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
//将buffer的数据写入到通道中
try {
dst.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
//创建一个服务器对象
GroupChatServer groupChatServer = new GroupChatServer();
groupChatServer.listen();
}
}
客户端
package com.zj.nio.DemoChatRoom;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;
/**
* @Author Zhou jian
* @Date 2020 ${month} 2020/6/5 0005 11:23
*/
public class GroupChatClient {
//定义相关属性
private final String HOST = "127.0.0.1";
//服务器的端口
private final int PORT = 6667;
private Selector selector;
private SocketChannel socketChannel;
private String userName;
//完成初始化工作
public GroupChatClient() throws IOException {
selector = Selector.open();
//连接服务器
socketChannel = socketChannel.open(new InetSocketAddress(HOST, PORT));
//设置非阻塞
socketChannel.configureBlocking(false);
//将channel注册到selector
socketChannel.register(selector, SelectionKey.OP_READ);
//得到userName
userName = socketChannel.getLocalAddress().toString().substring(1);
System.out.println(userName+"is ok.....");
}
//向服务器发送消息
public void sendInfo(String info){
info = userName+"说"+info;
try {
socketChannel.write(ByteBuffer.wrap(info.getBytes()));
}catch (IOException e){
e.printStackTrace();
}
}
//读取从服务端会送的消息
public void readInfo(){
try{
int readChannels = selector.select();
if(readChannels>0){//有可用的通道
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
if(key.isReadable()){
//得到相关的通道
SocketChannel channel = (SocketChannel)key.channel();
//得到一个buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读取数据到buffer
socketChannel.read(buffer);
//把读到的缓冲区的数据转换成字符擦混
String message = new String(buffer.array());
System.out.println(message.trim());
}
}
iterator.remove();//删除当前的selectionkey防止重复操作
}else{
// System.out.println("没有可用的通道");
}
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
//启动客户单
final GroupChatClient chatClient = new GroupChatClient();
//启动一个线程duqushj
new Thread(){
@Override
public void run() {
while (true){
chatClient.readInfo();
try {
Thread.currentThread().sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
//客户端发送数据到服务daunt
Scanner scanner = new Scanner(System.in);
while(scanner.hasNextLine()){
String s = scanner.nextLine();
chatClient.sendInfo(s);
}
}
}