? ? ? ? ? ? ? ?? ? ?本編文章意在循環(huán)漸進(jìn),可看最后一個(gè)就可以了
?文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-410041.html
?Selector簡(jiǎn)介
? 【1】創(chuàng)建Selector
? ? ? ? ? ? ?Selector selector = Selector.open();
? 【2】channel注冊(cè)到Selector
? ? ? ? ? ? ? ?首先channel必須是非阻塞的情況下
? ? ? ? ? ? ? ? channel.register(選擇器,操作的類型,綁定的組件);返回的是選擇鍵? ? ? ? ? ? ?
【3】輪詢查詢就緒操作


?【4】停止選擇的方法
NIO 編程步驟
代碼1.0
public class SelectorServer {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
//創(chuàng)建selector選擇器
Selector selector = Selector.open();
//將ssc注冊(cè)到選器器(建立兩者的聯(lián)系)
SelectionKey sscKey = ssc.register(selector, 0, null);
//選擇哪種監(jiān)聽(tīng)的事件
sscKey.interestOps(SelectionKey.OP_ACCEPT);
while (true){
selector.select();//阻塞方法,如果沒(méi)有事件發(fā)生,線程將在此處停止
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//返回所有可能發(fā)生事件的key集合(set)
while (iterator.hasNext()){
SelectionKey key = iterator.next();
System.out.println("key::"+key);
ServerSocketChannel channel = (ServerSocketChannel) key.channel();//獲取相對(duì)應(yīng)的channel
SocketChannel sc = channel.accept();
sc.configureBlocking(false);
SelectionKey scKey = sc.register(selector, 0, null);
System.out.println("scKey---->"+scKey);
scKey.interestOps(SelectionKey.OP_READ);
System.out.println("sc已經(jīng)在selector中注冊(cè)了!");
}
}
}
}
?結(jié)果分析:
? ? ?
?解決方案
【1】區(qū)分觸發(fā)seletor.select()的事件
【2】處理完一個(gè)事件,在對(duì)應(yīng)的注冊(cè)的keys集合刪除。
?代碼2.0
public class SelectorServer {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
Selector selector = Selector.open();
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
while (true){
selector.select();//阻塞方法,如果沒(méi)有事件發(fā)生,線程將在此處停止
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//返回所有可能發(fā)生事件的key集合(set)
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();//解決處理后事件在set集合中還有的現(xiàn)象
if (key.isAcceptable()){//區(qū)分不同事件觸發(fā)的結(jié)果
ServerSocketChannel channel = (ServerSocketChannel) key.channel();//獲取相對(duì)應(yīng)的channel
SocketChannel sc = channel.accept();
sc.configureBlocking(false);
SelectionKey scKey = sc.register(selector, 0, null);
System.out.println("scKey---->"+scKey);
scKey.interestOps(SelectionKey.OP_READ);
System.out.println("sc已經(jīng)在selector中注冊(cè)了!");
}else if (key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(32);
int len = channel.read(buffer);
buffer.flip();
System.out.println(StandardCharsets.UTF_8.decode(buffer).toString());
buffer.clear();
}
}
}
}
}
?新的問(wèn)題 如果ByteBuffer分配的空間不夠用會(huì)出現(xiàn)什么結(jié)果(分析)
? ? ? ?當(dāng)一次沒(méi)有讀完后就會(huì)觸發(fā)下一次的讀取,
?解決方案:為每一個(gè)連接客戶端的channel的ByteBuffer的空間動(dòng)態(tài)擴(kuò)容,(可以避免黏包半包的情況?。?/p>
?新的問(wèn)題:
當(dāng)客戶端強(qiáng)制關(guān)機(jī),服務(wù)器停止
?當(dāng)客戶端正常結(jié)束,服務(wù)器進(jìn)入無(wú)限循環(huán)
解決方案:
客戶端強(qiáng)制關(guān)機(jī),服務(wù)器報(bào)異常,使用try--catch語(yǔ)句? key.cancel()對(duì)此不做任何事情
客戶端正常結(jié)束,客戶端向服務(wù)器發(fā)送數(shù)據(jù),read = -1;
代碼3.0(最終篇)
public class SelectorServer {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(8080));
ssc.configureBlocking(false);
Selector selector = Selector.open();
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
while (true){
selector.select();//阻塞方法,如果沒(méi)有事件發(fā)生,線程將在此處停止
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();//返回所有可能發(fā)生事件的key集合(set)
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();//解決處理后事件在set集合中還有的現(xiàn)象
if (key.isAcceptable()){//區(qū)分不同事件觸發(fā)的結(jié)果
ServerSocketChannel channel = (ServerSocketChannel) key.channel();//獲取相對(duì)應(yīng)的channel
SocketChannel sc = channel.accept();
sc.configureBlocking(false);
SelectionKey scKey = sc.register(selector, 0, null);
System.out.println("scKey---->"+scKey);
ByteBuffer buffer = ByteBuffer.allocate(4);
scKey.attach(buffer);//為每一個(gè)注冊(cè)到set集合中的channel分配獨(dú)立的緩沖區(qū)
scKey.interestOps(SelectionKey.OP_READ);
System.out.println("sc已經(jīng)在selector中注冊(cè)了!");
}else if (key.isReadable()){
try {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
int read = channel.read(buffer);
// System.out.println("read::"+read);
// System.out.println("positon:"+buffer.position()+"limit:"+buffer.limit());
System.out.println(buffer);
if (read == -1){//客戶端正常結(jié)束,read的值等于-1
key.cancel();
continue;
}
if(read == buffer.capacity()){
ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity()*2);
newBuffer.flip();
newBuffer.put(buffer);
key.attach(newBuffer);
}else{
buffer.flip();
System.out.println(StandardCharsets.UTF_8.decode(buffer).toString());
buffer.clear();
}
} catch (IOException e) {
e.printStackTrace();
key.cancel();
}
}
}
}
}
}
結(jié)果:
客戶端正常結(jié)束
?客戶端強(qiáng)制結(jié)束
?
?Selector寫事件與讀事件類似
服務(wù)器
public class SelectorWriter {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress(8080));
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT, null);
while (true){
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()){
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
SelectionKey sckey = sc.register(selector, SelectionKey.OP_READ);
//發(fā)送大量的數(shù)據(jù)
StringBuilder str = new StringBuilder();
for (int i = 0; i < 300000; i++) {
str.append("a");
}
ByteBuffer buffer = Charset.defaultCharset().encode(str.toString());
int write = sc.write(buffer);
System.out.println(write);
if (buffer.hasRemaining()){
// 4. 關(guān)注可寫事件
sckey.interestOps(sckey.interestOps() + SelectionKey.OP_WRITE);
// sckey.interestOps(sckey.interestOps() | SelectionKey.OP_WRITE);
// 5. 把未寫完的數(shù)據(jù)掛到 sckey 上
sckey.attach(buffer);
}
}else if(key.isWritable()){
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
int write = sc.write(buffer);
System.out.println(write);
//清理工作
if (!buffer.hasRemaining()){
key.attach(null);
key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);
}
}
}
}
}
}
?客戶端
public class WriterClient {
public static void main(String[] args) throws Exception {
SocketChannel sc = SocketChannel.open();
sc.connect(new InetSocketAddress("localhost", 8080));
// 3. 接收數(shù)據(jù)
int count = 0;
while (true) {
ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
count += sc.read(buffer);
System.out.println(count);
buffer.clear();
}
}
}
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-410041.html
到了這里,關(guān)于Java 網(wǎng)絡(luò)編程之NIO(selector)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!