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) {
}
}
}