1.使用MappedByteBuffer复制超过2G的文件(理解)
1.图解
2.代码演示
package com.itheima.sh.filechannel_01;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
/*
将D:\上课视频.zip复制到F:\上课视频.zip
*/
public class FileChannelDemo01 {
public static void main(String[] args) throws Exception{
//1.创建访问随机文件的类的对象
RandomAccessFile f1 = new RandomAccessFile("D:\\上课视频.zip", "r");//只读
RandomAccessFile f2 = new RandomAccessFile("F:\\上课视频.zip", "rw");//读写
//2.获取通道
FileChannel c1 = f1.getChannel();
FileChannel c2 = f2.getChannel();
//3.获取文件大小
long size = c1.size();
// System.out.println("size = " + size);//2744459212
//4.定义变量保存每次复制的文件的大小
long everySize = 1024 * 1024 * 500;//500M 1024字节等于1KB 1024 * 1024 等于1M
//5.定义变量保存复制文件的次数
long count = (size % everySize == 0) ? size / everySize : size / everySize + 1;
//6.使用循环控制每次复制的代码
for (long i = 0; i < count; i++) {//i等于0表示第一次 1 表示第二次
//7.定义变量保存每次开始复制的起始索引
long start = everySize * i;
//8.定义变量保存每次复制的文件的真正大小
long trueSize = (size - start > everySize) ? everySize : size - start;
/*
abstract MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)
参数:
mode:表示读写模式
position:表示复制文件的起始位置
size:表示每次文件的大小
*/
//9.创建缓冲区
MappedByteBuffer m1 = c1.map(FileChannel.MapMode.READ_ONLY, start, trueSize);//只读
MappedByteBuffer m2 = c2.map(FileChannel.MapMode.READ_WRITE, start, trueSize);//读写
//10.读写数据
for (long l = 0; l < trueSize; l++) {
//获取
byte b = m1.get();
//存储
m2.put(b);
}
}
//释放资源
c2.close();
c1.close();
f2.close();
f1.close();
}
}
2.网络编程收发信息 (掌握)
1.客户端
package com.itheima.sh.net_channel_02;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/*
nio的客户端:
1.在nio中使用SocketChannel表示客户端套接字的通道
2.获取SocketChannel对象方法:
1)简单方式:
使用SocketChannel类中的静态方法:
static SocketChannel open(SocketAddress remote) 参数:remote属于SocketAddress类型,属于抽象类,我们使用子类创建对象
InetSocketAddress 类,构造方法:InetSocketAddress(String hostname, int port)
参数:
hostname:表示连接的服务器ip地址
port:表示连接的服务器的端口号
2)麻烦方式:
使用SocketChannel类中的静态方法:
static SocketChannel open();
使用SocketChannel类中的非静态方法:
boolean connect(SocketAddress remote)
参数:remote属于SocketAddress类型,属于抽象类,我们使用子类创建对象
InetSocketAddress 类,构造方法:InetSocketAddress(String hostname, int port)
参数:
hostname:表示连接的服务器ip地址
port:表示连接的服务器的端口号
3.写方法:
int write(ByteBuffer src) 将字节序列从给定的缓冲区中写入此通道
abstract int read(ByteBuffer dst) 将字节序列从此通道中读入给定的缓冲区。
*/
public class ClientDemo01 {
public static void main(String[] args) throws IOException {
//1.创建客户端通道对象连接服务器 static SocketChannel open(SocketAddress remote)
//InetSocketAddress(String hostname, int port)
SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999));
//2.向服务器写数据
//2.1创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//2.2向缓冲区中添加数据
buffer.put("hello,我来了".getBytes());
//2.3切换读模式 limit拿到position位置 position拿到0位置 清除mark
buffer.flip();
// int write(ByteBuffer src) 将字节序列从给定的缓冲区中写入此通道
sc.write(buffer);
//关闭流
sc.close();
}
}
小结:
1.创建客户端对象:
使用SocketChannel类中的静态方法:
static SocketChannel open(SocketAddress remote) 参数:remote属于SocketAddress类型,属于抽象类,我们使用子类创建对象
InetSocketAddress 类,构造方法:InetSocketAddress(String hostname, int port)
参数:
hostname:表示连接的服务器ip地址
port:表示连接的服务器的端口号
- int write(ByteBuffer src) 将字节序列从给定的缓冲区中写入此通道
- abstract int read(ByteBuffer dst) 将字节序列从此通道中读入给定的缓冲区。
2.服务器端
package com.itheima.sh.net_channel_02;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
/*
服务器端:
1.在nio中使用ServerSocketChannel表示服务器端套接字的通道
2.创建ServerSocketChannel对象:
1)使用ServerSocketChannel类的方法:static ServerSocketChannel open()
2)使用ServerSocketChannel对象调用父类的绑定端口号方法:
ServerSocketChannel bind(SocketAddress local) 将通道的套接字绑定到本地地址,并配置套接字以
参数:SocketAddress属于抽象类,使用子类InetSocketAddress(int port) 指定服务器的端口号
3.使用服务器的对象调用方法获取客户端通道对象:
abstract SocketChannel accept() 接受到此通道套接字的连接。
4.使用侦听的客户端对象调用客户端中的读取方法:
int read(ByteBuffer dst)
*/
public class ServerDemo02 {
public static void main(String[] args) throws IOException {
//1.创建ServerSocketChannel对象
//1.1 使用ServerSocketChannel类的方法:static ServerSocketChannel open()
ServerSocketChannel ssc = ServerSocketChannel.open();
//1.2 ServerSocketChannel bind(SocketAddress local)
//使用子类InetSocketAddress(int port) 指定服务器的端口号
ssc.bind(new InetSocketAddress(9999));
//2.使用服务器的对象调用方法获取客户端通道对象:abstract SocketChannel accept() 接受到此通道套接字的连接。
System.out.println("1111");
//阻塞到这里了,等待客户端访问。
SocketChannel sc = ssc.accept();
System.out.println("2222");
//3.读取数据 int read(ByteBuffer dst)
ByteBuffer buffer = ByteBuffer.allocate(1024);
int len = sc.read(buffer);//读取个数
//4.将buffer转换为普通数组
byte[] arr = buffer.array();
//5.输出客户端数据
//这里操作的是普通数组arr,不是缓冲区,所以不用切换读模式
System.out.println(new String(arr,0,len));
//6.释放资源
sc.close();
ssc.close();
}
}
小结:
1.创建服务器套接字对象:
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(9999));
2.abstract SocketChannel accept() 接受到此通道套接字的连接。
3.解决上述accept阻塞的问题
package com.itheima.sh.net_channel_02;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class ClientDemo03 {
public static void main(String[] args) throws IOException {
//1.创建客户端通道对象连接服务器 static SocketChannel open(SocketAddress remote)
//InetSocketAddress(String hostname, int port)
SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 12306));
//关闭流
sc.close();
}
}
package com.itheima.sh.net_channel_02;
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
/*
解决accept方法阻塞问题:
*/
public class ServerDemo03 {
public static void main(String[] args) throws Exception{
//1.创建ServerSocketChannel对象
//1.1 使用ServerSocketChannel类的方法:static ServerSocketChannel open()
ServerSocketChannel ssc = ServerSocketChannel.open();
//1.2 ServerSocketChannel bind(SocketAddress local)
//使用子类InetSocketAddress(int port) 指定服务器的端口号
ssc.bind(new InetSocketAddress(12306));
//2.设置非阻塞模式:
/*
abstract SelectableChannel configureBlocking(boolean block) 调整此通道的阻塞模式。
参数是false表示非阻塞
*/
ssc.configureBlocking(false);
//3.获取客户端
//由于这里设置了非阻塞模式,那么如果没有客户端,该accept方法返回null
// System.out.println("111111");
// SocketChannel sc = ssc.accept();
// System.out.println("222222"+sc);
//使用死循环
while(true){
SocketChannel sc = ssc.accept();
//判断sc是否等于null
if(sc == null){
//说明没有客户端
System.out.println("没有客户端连接,玩会");
Thread.sleep(2000);
}else{
//说明有客户端
System.out.println("连接上了客户端");
break;
}
}
}
}
小结:
我们可以使用ServerSocketChannel的父类中的方法设置为服务器为非阻塞方式:
abstract SelectableChannel configureBlocking(boolean block) 调整此通道的阻塞模式。
参数是false表示非阻塞
3.Selector选择器(掌握)
1.选择器介绍
nio三大组件:
1.buffer 缓冲区 负责存储数据
2.channel 通道 负责建立连接
3.selector 选择器 主要负责一个选择器可以监听多个通道 是非阻塞的核心
阻塞模式:
非阻塞模式:
小结:
1.使用了多路复用,只需要一个线程就可以处理多个通道,降低内存占用率,减少CPU切换时间,在高并发、高频段业务环境下有非常重要的优势
2.多路复用:一个selector可以监听多个服务器端口号
2.Selector选择器的使用
package com.itheima.sh.selector_03;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
/*
客户端
*/
public class ClientDemo01 {
public static void main(String[] args) throws IOException {
//1.创建客户端通道对象
SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 12306));
//释放资源
sc.close();
}
}
package com.itheima.sh.selector_03;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Set;
/*
选择器的使用:
1.选择器使用Selector表示,属于抽象类
2.获取选择器:
static Selector open() 打开一个选择器。
3.将连接通道注册到选择器:
使用服务器端的套接字通道ServerSocketChannel的父类SelectableChannel中的注册方法:
SelectionKey register(Selector sel, int ops) 向给定的选择器注册此通道,返回一个选择键。
参数:
sel:表示被注册的选择器
ops:所得键的可用操作集 所得键就是SelectionKey,该类表示 SelectableChannel(通道) 在 Selector(选择器) 中的注册的标记
SelectionKey选择键的成员变量:
static int OP_ACCEPT 用于套接字【接受】操作的操作集位。
说明:我们使用服务器套接字ServerSocketChannel和客户端建立连接,这里必须指定 OP_ACCEPT,否则就会报错
static int OP_CONNECT 用于套接字【连接】操作的操作集位。
static int OP_READ 用于【读取】操作的操作集位。
static int OP_WRITE 用于【写入】操作的操作集位。
4.注册到选择器上的channel必须是非阻塞模式,通过ServerSocketChannel的父类SelectableChannel方法:
abstract SelectableChannel configureBlocking(boolean block) false表示非阻塞
5.方法:abstract int select() 选择一组键,其相应的通道已为 I/O 操作准备就绪
说明:等待客户端访问,如果没有客户端访问,那么此时一直等待客户端,只要有客户端访问,如果
服务器不做处理,那么就不会等待了。
返回值表示获取到的客户端数量
*/
public class ServerDemo01 {
public static void main(String[] args) throws Exception {
//1.创建服务器的通道
ServerSocketChannel ssc = ServerSocketChannel.open();
//2.绑定端口号
ssc.bind(new InetSocketAddress(12306));
//3.设置为非阻塞模式 abstract SelectableChannel configureBlocking(boolean block) false表示非阻塞
ssc.configureBlocking(false);
//4.获取选择器
Selector selector = Selector.open();
//5.将通道注册到选择器上
/*
使用服务器端的套接字通道ServerSocketChannel的父类SelectableChannel中的注册方法:
SelectionKey register(Selector sel, int ops) 向给定的选择器注册此通道,返回一个选择键。
注册方法第二个参数:
ops:所得键的可用操作集 所得键就是SelectionKey,该类表示 SelectableChannel(通道) 在
Selector(选择器) 中的注册的标记
SelectionKey选择键的成员变量:
static int OP_ACCEPT 用于套接字【接受】操作的操作集位。
*/
ssc.register(selector, SelectionKey.OP_ACCEPT);//表示将通道ssc注册到选择器上,就是将侦听并获取客户端的accept()方法交给了选择器
/*
方法:abstract int select() 选择一组键,其相应的通道已为 I/O 操作准备就绪
说明:等待客户端访问,如果没有客户端访问,那么此时一直等待客户端,只要有客户端访问,如果
服务器不做处理,那么就不会等待了。
返回值表示获取到的客户端数量
*/
/* System.out.println("1");
//6.使用选择器对象调用选择器类的方法abstract int select() 等待客户端
int count = selector.select();
System.out.println("2");
System.out.println("count = " + count);*/
while(true){
System.out.println("1");
//6.使用选择器对象调用选择器类的方法abstract int select() 等待客户端
/*
只要有客户端访问,如果服务器不做处理,那么就不会等待了。
如果在服务器中处理了客户端,那么select()方法就会等待下个客户端访问
*/
int count = selector.select();
//休眠
Thread.sleep(2000);
System.out.println("2");
System.out.println("count = " + count);
/*
处理客户端
*/
Set<SelectionKey> keys = selector.selectedKeys();
for (SelectionKey key : keys) {
ServerSocketChannel ssc2 = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc2.accept();
}
}
}
}
小结:
选择器使用步骤:
1.创建服务器通道:
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(12306));
2.设置为非阻塞
ssc.configureBlocking(false);
3.创建选择器
Selector selector = Selector.open();
4.将通道注册到选择器上
ssc.register(selector, SelectionKey.OP_ACCEPT);
5.使用选择器操作通道
3.Selector选择器方法
- abstract Set selectedKeys() :当客户端来连接服务器之时,Selector会把【被连接】的服务器对象放到Set集合中。
说明:
1.SelectionKey表示 SelectableChannel
(通道) 在 Selector
(选择器)中的注册的关系。 是一个类
-
Set keys() 将服务器的所用对象放到set集合中
-
代码演示:
package com.itheima.sh.selector_04; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.util.Set; /* 1.Selector选择器中的方法: 1)abstract Set<SelectionKey> selectedKeys() :当客户端来连接服务器之时,Selector会把被连接的**服务器对象**放到Set集合中。 2)Set<SelectionKey> keys() 将服务器的所用对象放到set集合中 */ public class ServerDemo01 { public static void main(String[] args) throws IOException { //1.创建服务器对象 ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.bind(new InetSocketAddress(9999)); ServerSocketChannel ssc2 = ServerSocketChannel.open(); ssc2.bind(new InetSocketAddress(8888)); //2.设置为非阻塞模式 ssc.configureBlocking(false); ssc2.configureBlocking(false); //3.获取选择器 Selector selector = Selector.open(); //4.将通道ssc注册到选择器上 //多个通道注册到同一个选择器上 ssc.register(selector, SelectionKey.OP_ACCEPT); ssc2.register(selector, SelectionKey.OP_ACCEPT); //5.abstract Set<SelectionKey> selectedKeys() :当客户端来连接服务器之时,Selector会把被连接的**服务器对象**放到Set集合中。 Set<SelectionKey> set1 = selector.selectedKeys(); //set1集合中的服务器对象个数:0 System.out.println("set1集合中的服务器对象个数:"+set1.size()); //获取所有的服务器对象 Set<SelectionKey> set2 = selector.keys(); //set2集合中的服务器对象个数:2 System.out.println("set2集合中的服务器对象个数:"+set2.size()); //6.调用select方法连接客户端 selector.select(); //输出 //set集合中的服务器对象个数:1 System.out.println("set集合中的服务器对象个数:"+set1.size()); //set2集合中的服务器对象个数:2 System.out.println("set2集合中的服务器对象个数:"+set2.size()); } }
小结:
1.Set selectedKeys() 将被连接的服务器对象放到set集合中
2.Set keys() 将服务器的所用对象放到set集合中
4.使用Selector选择器接收来自客户端的数据并打印服务器端
客户端:
package com.itheima.sh.selector_05;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/*
客户端
*/
public class ClientDemo01 {
public static void main(String[] args) throws IOException {
//1.创建客户端对象
SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 7777));
//2.创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//3.添加数据
buffer.put("大家好,我是nio,我来了,我难吗".getBytes());
//4.切换读模式
buffer.flip();
//5.写数据
sc.write(buffer);
//6.释放资源
sc.close();
}
}
服务器端:
package com.itheima.sh.selector_05;
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.Set;
/*
服务器端
使用Selector选择器接收来自客户端的数据并打印服务器端
*/
public class ServerDemo01 {
public static void main(String[] args) throws IOException {
//1.创建服务器对象
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(7777));
//2.设置非阻塞
ssc.configureBlocking(false);
//3.创建选择器
Selector selector = Selector.open();
//4.将通道注册到选择其实上
ssc.register(selector, SelectionKey.OP_ACCEPT);
//5.连接客户端
selector.select();
//6.获取被连接的服务器对象放到set集合
Set<SelectionKey> set1 = selector.selectedKeys();
System.out.println("被连接的服务器对象个数:"+set1.size());
//7.遍历集合
for (SelectionKey key : set1) {//key属于SelectionKey类型表示通道ssc和选择器之间的关系
//8.abstract SelectableChannel channel()返回为之创建此键的通道。 SelectableChannel 是ServerSocketChannel的父类
ServerSocketChannel ss = (ServerSocketChannel) key.channel();//表示服务器通道
System.out.println("11111");
//9.获取客户端通道
SocketChannel sc = ss.accept();
System.out.println("22222");
//10.创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//11.读取客户端发送的数据
int len = sc.read(buffer);
//12.将buffer转换为普通数组
byte[] arr = buffer.array();
//13.输出客户端发送的数据
System.out.println(new String(arr,0,len));
}
//14.释放资源
ssc.close();
}
}
5.Selector选择器管理多个通道
客户端:
package com.itheima.sh.selector_06;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/*
客户端
*/
public class ClientDemo01 {
public static void main(String[] args) throws IOException {
//1.创建客户端对象
SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999));
//2.创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//3.添加数据
buffer.put("大家好,我是nio,我来了,我难吗".getBytes());
//4.切换读模式
buffer.flip();
//5.写数据
sc.write(buffer);
//6.释放资源
sc.close();
}
}
服务器:
package com.itheima.sh.selector_06;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Set;
/*
服务器端
Selector选择器管理多个通道
*/
public class ServerDemo01 {
public static void main(String[] args) throws IOException {
//1.创建服务器对象
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(7777));
ServerSocketChannel ssc1 = ServerSocketChannel.open();
ssc1.bind(new InetSocketAddress(8888));
ServerSocketChannel ssc2 = ServerSocketChannel.open();
ssc2.bind(new InetSocketAddress(9999));
//2.设置非阻塞
ssc.configureBlocking(false);
ssc1.configureBlocking(false);
ssc2.configureBlocking(false);
//3.创建选择器
Selector selector = Selector.open();
//4.将通道注册到选择器上
ssc.register(selector, SelectionKey.OP_ACCEPT);
ssc1.register(selector, SelectionKey.OP_ACCEPT);
ssc2.register(selector, SelectionKey.OP_ACCEPT);
//加死循环模拟一直运行
while(true){
//5.连接客户端
System.out.println("11111");
selector.select();
//6.获取所有被连接的服务器对象
Set<SelectionKey> set1 = selector.selectedKeys();
// System.out.println("服务器个数:"+set1.size());
// //7.遍历set集合
for (SelectionKey key : set1) {
//8.获取服务器对象
ServerSocketChannel ss = (ServerSocketChannel) key.channel();
//9.取出客户端
SocketChannel s = ss.accept();
System.out.println("s = " + s);
//10.定义缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//11.使用客户端对象s调用读取方法读取客户端的数据放到buffer中
int len = s.read(buffer);
//12.转换普通数组
byte[] arr = buffer.array();
//13.输出
System.out.println(new String(arr,0,len));
}
}
}
}
问题图解:
产生上述异常原因:
Selector把被连接的服务器对象放在了一个Set集合中,但是使用完后并没有删除。导致在遍历集合时,遍历到了已经没用的对象,出现了异常。
解决上述问题:
package com.itheima.sh.selector_06;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
/*
服务器端
Selector选择器管理多个通道
*/
public class ServerDemo01 {
public static void main(String[] args) throws IOException {
//1.创建服务器对象
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(7777));
ServerSocketChannel ssc1 = ServerSocketChannel.open();
ssc1.bind(new InetSocketAddress(8888));
ServerSocketChannel ssc2 = ServerSocketChannel.open();
ssc2.bind(new InetSocketAddress(9999));
//2.设置非阻塞
ssc.configureBlocking(false);
ssc1.configureBlocking(false);
ssc2.configureBlocking(false);
//3.创建选择器
Selector selector = Selector.open();
//4.将通道注册到选择器上
ssc.register(selector, SelectionKey.OP_ACCEPT);
ssc1.register(selector, SelectionKey.OP_ACCEPT);
ssc2.register(selector, SelectionKey.OP_ACCEPT);
//加死循环模拟一直运行
while (true) {
//5.连接客户端
System.out.println("11111");
selector.select();
//6.获取所有被连接的服务器对象
Set<SelectionKey> set1 = selector.selectedKeys();
System.out.println("服务器个数:" + set1.size());
// //7.遍历set集合
/*
对于set1集合来说,取出每个服务器对象使用完毕之后需要将其从set集合中删除
注意删除集合中的数据,我们这里是增强for循环,原理是Iterator迭代器,删除集合数据不能使用集合中的删除方法,
否则会报并发修改异常,使用Iterator迭代器中的删除方法
*/
//获取迭代器对象
Iterator<SelectionKey> it = set1.iterator();
while (it.hasNext()) {
//取出SelectionKey
SelectionKey key = it.next();
// for (SelectionKey key : set1) {//8888 9999
//8.获取服务器对象
ServerSocketChannel ss = (ServerSocketChannel) key.channel();
//9.取出客户端
SocketChannel s = ss.accept();
System.out.println("s = " + s);
//10.定义缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//11.使用客户端对象s调用读取方法读取客户端的数据放到buffer中
int len = s.read(buffer);
//12.转换普通数组
byte[] arr = buffer.array();
//13.输出
System.out.println(new String(arr, 0, len));
//删除服务器对象 使用迭代器中的删除方法
it.remove();
// }
}
//
}
}
}
小结:每次处理完客户端之后,都使用迭代器中的删除方法将对应的服务器对象从set集合中删除。
4.NIO2-AIO(异步、非阻塞)(理解)
概念介绍
从jdk7开始引入的技术,称为NIO2英文全称:Asynchronous I/O 异步的IO.异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
1.同步:调用方法要有返回值。
2.异步:调用方法没有返回值,并且可以支持回调函数。回调函数就是回过头来在调用的函数,有底层调用的,我们只负责编写代码。
3.阻塞:不执行其他操作,一直等待。
4.非阻塞:不一直等待,可以执行其他操作。
上述四个概念举例:
AIO同步写法【听下就可以了,用不到】
客户端:
package com.itheima.sh.aio_07;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/*
客户端
*/
public class ClientDemo01 {
public static void main(String[] args) throws IOException {
//1.创建客户端对象
SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 12306));
//2.创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//3.添加数据
buffer.put("大家好,我是Aio,我来了,我难吗".getBytes());
//4.切换读模式
buffer.flip();
//5.写数据
sc.write(buffer);
//6.释放资源
sc.close();
}
}
服务端:
package com.itheima.sh.aio_07;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/*
服务器:
AIO同步(有返回值)写法
使用步骤:
1.创建aio的服务器AsynchronousServerSocketChannel的对象:
static AsynchronousServerSocketChannel open() 打开异步服务器套接字通道。
AsynchronousServerSocketChannel bind(SocketAddress local) 绑定端口 SocketAddress属于抽象类,我们使用子类InetSocketAddress(端口号)
2.侦听并获取客户端套接字:
abstract Future<AsynchronousSocketChannel> accept() 接受连接
说明:
1)该方法将接收的客户端存储到Future<V>接口中,需要使用该接口中的V get() 取出客户端
2) AsynchronousSocketChannel表示客户端套接字
3)上述accept方法有返回值,属于同步的
3.使用客户端套接字对象调用方法读取客户端的数据
abstract Future<Integer> read(ByteBuffer dst) 从该通道读取到给定缓冲区的字节序列。
说明:
1)该方法将接收的客户端的数据存储到Future<V>接口中,需要使用该接口中的V get() 取出客户端请求的 数据
2)上述read方法有返回值,属于同步
*/
public class ServerDemo01 {
public static void main(String[] args) throws Exception {
//1.创建AIO的服务器对象
AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open();
//2.绑定端口号
assc.bind(new InetSocketAddress(12306));
//3.侦听并获取客户端套接字:
//future中存放的是客户端套接字AsynchronousSocketChannel对象,我们需要调用get方法获取
Future<AsynchronousSocketChannel> f = assc.accept();
AsynchronousSocketChannel ascoket = f.get();
//4.使用客户端套接字对象调用方法读取客户端的数据
// abstract Future<Integer> read(ByteBuffer dst) 从该通道读取到给定缓冲区的字节序列。
ByteBuffer buffer = ByteBuffer.allocate(1024);
Future<Integer> f2 = ascoket.read(buffer);
//取出数据
Integer len = f2.get();//读取的字节个数
//5.将字节缓冲区变为普通数组
byte[] arr = buffer.array();
//6.输出
System.out.println(new String(arr,0,len));
ascoket.close();
assc.close();
}
}
小结:
1.AsynchronousServerSocketChannel表示AIO中的服务器套接字对象
2.获取对象:
static AsynchronousServerSocketChannel open() 打开异步服务器套接字通道。
AsynchronousServerSocketChannel bind(SocketAddress local) 绑定端口
3.侦听并获取客户端套接字:
abstract Future<AsynchronousSocketChannel> accept() 接受连接
4.使用客户端套接字对象调用方法读取客户端的数据
abstract Future<Integer> read(ByteBuffer dst) 从该通道读取到给定缓冲区的字节序列。
AIO异步非阻塞连接(理解)
客户端:
package com.itheima.sh.aio_08;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/*
客户端
*/
public class ClientDemo01 {
public static void main(String[] args) throws IOException {
//1.创建客户端对象
SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999));
//6.释放资源
sc.close();
}
}
服务器:
package com.itheima.sh.aio_08;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.Future;
/*
服务器
编写AIO的异步方式:
*/
public class ServerDemo01 {
public static void main(String[] args) throws Exception {
//1.创建AIO的服务器对象
AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open();
assc.bind(new InetSocketAddress(9999));
//2.异步非阻塞接收客户端
/*
abstract <A> void accept(A attachment, CompletionHandler<AsynchronousSocketChannel> handler)
参数:
attachment:要附加到I / O操作的对象; 可以是null
handler:属于CompletionHandler接口类型,表示消耗结果的处理程序 ,这里保存的是客户端套接字
方法:
void completed(V result, A attachment) 操作完成后调用。 回调函数
void failed(Throwable exc, A attachment) 当操作失败时调用。
*/
System.out.println("111111");
assc.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
// 操作完成后调用。 回调函数:回过头来调用的函数,底层调用的
/*
result:表示客户端
attachment:就是null
*/
@Override
public void completed(AsynchronousSocketChannel result, Object attachment) {
System.out.println("completed.......");
}
// 当操作失败时调用
@Override
public void failed(Throwable exc, Object attachment) {
}
});
System.out.println("222222");
//为了能够让上述回调函数执行,这里使用死循环,让jvm一直运行
while (true){
}
}
}
AIO异步非阻塞连接并读取客户端的数据(理解)
客户端:
package com.itheima.sh.aio_09;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
/*
客户端
*/
public class ClientDemo01 {
public static void main(String[] args) throws IOException {
//1.创建客户端对象
SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 12306));
//2.创建缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
//3.添加数据
buffer.put("大家好,我是Aio,我来了,我难吗".getBytes());
//4.切换读模式
buffer.flip();
//5.写数据
sc.write(buffer);
//6.释放资源
sc.close();
}
}
服务器端:
package com.itheima.sh.aio_09;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.Future;
/*
服务器:
AIO异步,读取客户端的数据
*/
public class ServerDemo01 {
public static void main(String[] args) throws Exception {
//1.创建服务器对象
AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open();
assc.bind(new InetSocketAddress(12306));
//2.异步非阻塞接收客户端
System.out.println(1);
assc.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
//接收的客户端
//socket表示客户端
@Override
public void completed(AsynchronousSocketChannel socket, Object attachment) {
System.out.println(3);
//如果想要异步读取客户端数据
/*
<A> void read(ByteBuffer dst, A attachment, CompletionHandler<Integer,? super A> handler)
参数:
dst:要传输字节的缓冲区 存储数据的字节缓冲区
attachment:要附加到I / O操作的对象; 可以是null
handler:完成处理程序,属于CompletionHandler接口类型
抽象方法:
void completed(V result, A attachment) 操作完成后调用。
void failed(Throwable exc, A attachment) 当操作失败时调用。
*/
//创建字节数组
ByteBuffer buffer = ByteBuffer.allocate(1024);
socket.read(buffer, null, new CompletionHandler<Integer, Object>() {
//读取成功的方法
//len表示read读取的字节个数
@Override
public void completed(Integer len, Object attachment) {
System.out.println(5);
//将字节缓冲区变为普通的字节数组
byte[] arr = buffer.array();
System.out.println(new String(arr,0,len));
}
@Override
public void failed(Throwable exc, Object attachment) {
}
});
System.out.println(4);
}
@Override
public void failed(Throwable exc, Object attachment) {
}
});
System.out.println(2);
//模拟服务器一直运行
while (true) {
}
}
}