一、基本概念
選擇器提供一種選擇執(zhí)行已經(jīng)就緒的任務(wù)的能力。selector選擇器可以讓單線程處理多個(gè)通道。如果程序打開了多個(gè)連接通道,每個(gè)連接的流量都比較低,可以使用Selector對通道進(jìn)行管理。
二、如何創(chuàng)建選擇器
1.創(chuàng)建Selector
Selector selector = Selector.open();
2.必須將通道設(shè)置為非阻塞模式才能注冊到選擇器上
Channel.configureBlocking(false);
3.把通道注冊到選擇器上,會返回一個(gè)選擇鍵
SelectionKey selectionKey = socketChannel.register(selector, SelectionKey.OP_READ);
SelectionKey的操作有:
- SelectionKey.OP_CONNECT,指某個(gè)通道連接到服務(wù)器
- SelectionKey.OP_ACCEPT,只有ServerSocketChannel有這個(gè)事件,查看是否有新的連接
- SelectionKey.OP_READ,是否有可讀的通道就緒
- SelectionKey.OP_WRITE,寫數(shù)據(jù)的通道是否就緒
注冊完成后,可以調(diào)用select()方法輪詢是否有就緒的通道
int count = selector.select();
select()方法,返回就緒的通道數(shù)量
三、服務(wù)器端模板
//服務(wù)器端模板代碼
public static void Server_Standard_Code_template() {
try {
ServerSocketChannel ssc=ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress("localhost",80));
//只有設(shè)置為非阻塞才能注冊到選擇器中
ssc.configureBlocking(false);
//創(chuàng)建一個(gè)選擇器
Selector selector = Selector.open();
//通道注冊進(jìn)選擇器中---監(jiān)聽客戶端連接事件
ssc.register(selector, SelectionKey.OP_ACCEPT);
while(true){
//獲取以及就緒的通道數(shù)量
int select = selector.select();
//沒有通道就緒
if(select==0)
{
continue;
}
//獲取已經(jīng)就緒的
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext())
{
SelectionKey selectionKey = iterator.next();
//客戶端連接請求事件
if(selectionKey.isAcceptable())
{
//接收連接
}else if(selectionKey.isReadable())
{
//讀取數(shù)據(jù)
}
else if(selectionKey.isWritable())
{
//寫數(shù)據(jù)
}
//移除
iterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
四、NIO通訊實(shí)例
服務(wù)器端文章來源:http://www.zghlxwxcb.cn/news/detail-838529.html
public class NIOServer {
//通道管理器
private Selector selector;
/**
* 獲取一個(gè)ServerSocket通道,并對該通道做一些初始化工作
* @param port 端口號
* @throws IOException
*/
public void initServer(int port) throws IOException {
//獲取一個(gè)ServerSocket通道
ServerSocketChannel socketChannel = ServerSocketChannel.open();
//設(shè)置通道為非阻塞
socketChannel.configureBlocking(false);
//將通道對應(yīng)的ServerSocket綁定到port端口
socketChannel.socket().bind(new InetSocketAddress(port));
//獲取一個(gè)通道管理器
this.selector = Selector.open();
/**
* 將通道管理器和該通道綁定,并為該通道注冊SelectionKey.OP_ACCEPT事件
* 注冊該事件后,當(dāng)該事件到達(dá)時(shí),selector.select()會返回
* 如果該事件沒有到達(dá),selector.select()會一直阻塞
*/
socketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void listen() throws IOException {
while (true){
//當(dāng)注冊的事件到達(dá)時(shí),方法返回,否則該方法一直阻塞
selector.select();
//獲取selector中選項(xiàng)的迭代器
Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
//刪除已經(jīng)選擇的key,防止重復(fù)處理
iterator.remove();
//客戶端連接請求事件
if(key.isAcceptable()){
//接收連接
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
//獲取客戶端連接的通道
SocketChannel channel = serverSocketChannel.accept();
//設(shè)置為非阻塞
channel.configureBlocking(false);
//向客戶端發(fā)送數(shù)據(jù)源
ByteBuffer buf = ByteBuffer.allocate(1024);
String message = "你好我是服務(wù)器端,我接收到了你的消息";
buf.put(message.getBytes(StandardCharsets.UTF_8));
//把緩沖區(qū)切換成讀取模式
buf.flip();
//將buffer寫入channel
while (buf.hasRemaining()){
channel.write(buf);
}
//和客戶端連接成功后,為了接收到客戶端的信息,需要給通道設(shè)置讀取權(quán)限
channel.register(this.selector,SelectionKey.OP_READ);
}else if(key.isReadable()){
//讀取數(shù)據(jù)
read(key);
}
}
}
}
public void read(SelectionKey key) throws IOException {
//得到事件發(fā)生的socket通道
SocketChannel channel = (SocketChannel) key.channel();
//創(chuàng)建讀取的緩沖區(qū)
ByteBuffer buffer = ByteBuffer.allocate(1024);
//將數(shù)據(jù)讀取到緩沖區(qū)
channel.read(buffer);
// 4、把緩沖區(qū)切換成寫出模式
buffer.flip();
String rs = new String(buffer.array(),0,buffer.remaining());
System.out.println(rs);
}
public static void main(String[] args) throws IOException {
NIOServer server = new NIOServer();
server.initServer(8100);
server.listen();
}
}
客戶端文章來源地址http://www.zghlxwxcb.cn/news/detail-838529.html
public class NIOClient {
//通道管理器
private Selector selector;
public static void main(String[] args) throws IOException {
NIOClient client = new NIOClient();
client.initClick("127.0.0.1",8100);
client.listen();
}
public void initClick(String ip,int port) throws IOException {
//獲取一個(gè)socket
SocketChannel channel = SocketChannel.open();
//設(shè)置通道為非阻塞
channel.configureBlocking(false);
//獲取一個(gè)通道管理器
this.selector = Selector.open();
channel.connect(new InetSocketAddress(ip,port));
channel.register(this.selector, SelectionKey.OP_CONNECT);
}
public void listen() throws IOException {
while (true){
selector.select();
Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();
if(key.isConnectable()){
SocketChannel channel = (SocketChannel) key.channel();
if(channel.isConnectionPending()){
channel.finishConnect();
}
//設(shè)置為阻塞
channel.configureBlocking(false);
//向客戶端發(fā)送數(shù)據(jù)源
ByteBuffer buffer = ByteBuffer.allocate(1024);
String message = "服務(wù)器端你好,我是客戶端";
buffer.put(message.getBytes(StandardCharsets.UTF_8));
//把緩沖區(qū)切換成讀取模式
buffer.flip();
//將buffer寫入channel
while (buffer.hasRemaining()){
channel.write(buffer);
}
channel.register(this.selector,SelectionKey.OP_READ);
}else if(key.isReadable()){
read(key);
}
}
}
}
public void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);
byte[] data = buffer.array();
// 4、把緩沖區(qū)切換成寫出模式
buffer.flip();
String rs = new String(data,0,buffer.remaining());
System.out.println(rs);
}
}
到了這里,關(guān)于NIO核心三:Selector的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!