《BIO與NIO的區別》


IO、

    就是說用什么樣的通道進行數據的發送和接收,Java共支持3種網絡編程IO模式:BIO,NIO,AIO, 我這里主要講解BIO與NIO;

BIO、

    BIO  同步阻塞模型,一個客戶端連接處理對應一個線程;

 

代碼如下:

package com.tuling.xueyuan.io;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class SocketServer {

public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9000);
while (true){
System.out.println("等待連接");
Socket clientSocket = serverSocket.accept();
System.out.println("有客戶端連接了。。。");
// handler(clientSocket);
new Thread(new Runnable() {
@Override
public void run() {
try {
handler(clientSocket);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}

}


private static void handler(Socket clienSocket) throws IOException{
byte[] bytes = new byte[1024];
System.out.println("准備read....");
int read = clienSocket.getInputStream().read(bytes);
System.out.println("read完畢。。。。");
if (read != -1) {
System.out.println("接收到客戶端的數據:" + new String(bytes, 0, read));
}
clienSocket.getOutputStream().write("HelloClient".getBytes());
clienSocket.getOutputStream().flush();
}
}


啟動以上服務端代碼,然后通過cmd打開黑窗口, 執行telnet localhost 9000 連接服務端 通過Ctrl+] 進入到客戶端 通過send 發送數據
經過測試,等到如下結果
1、當沒有客戶端進行連接時, 代碼到Socket clientSocket = serverSocket.accept();這里就會阻塞,不會在向下執行;
2、一個客戶端連接處理對應一個線程,服務端只能接受客戶端一次數據;
3、一個客戶端連接處理對應一個線程,線程的開銷太大了
4、假如A客戶端連接啟動了a線程,但是A客戶端長時間沒有進行發送數據,那么a線程就一直在阻塞,占用cpu空間,如果是10000個客戶端,只有1000個連接並且發送數據,
但是9000個客戶端只是連接沒有發送數據,這將是一件崩潰的事件;
根據以上BIO的缺點,就衍生出了NIO

NIO、
同步非阻塞的模型,運用了多路復用器,實現了一個線程可以處理多個客戶端的響應;

 

 總結: 創建了一個Selector(多路復用器),服務端向Selector注冊(連接事件),客戶端也向Selector注冊(讀事件),當客戶端向服務端連接時,觸發了連接事件,這時底層操作系統就會把有事件的存入到就緒列表,然后判斷當時連接事件,就遍歷只有連接事件的客戶端,進行連接,如果是讀事件,那么就只讀取有讀事件的數據。

代碼如下:

package com.tuling.xueyuan.io;

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.Iterator;
import java.util.Set;

public class NioSelectorServer {

public static void main(String[] args) throws IOException, InterruptedException {

// 創建NIO ServerSocketChannel
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(9000));
// 設置ServerSocketChannel為非阻塞
serverSocket.configureBlocking(false);
// 打開Selector處理Channel,即創建epoll
Selector selector = Selector.open();
// 把ServerSocketChannel注冊到selector上,並且selector對客戶端accept連接操作感興趣
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服務啟動成功");

while (true) {
// 阻塞等待需要處理的事件發生
selector.select();

// 獲取selector中注冊的全部事件的 SelectionKey 實例
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();

// 遍歷SelectionKey對事件進行處理
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
// 如果是OP_ACCEPT事件,則進行連接獲取和事件注冊
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
// 這里只注冊了讀事件,如果需要給客戶端發送數據可以注冊寫事件
socketChannel.register(selector, SelectionKey.OP_READ);
System.out.println("客戶端連接成功");
} else if (key.isReadable()) { // 如果是OP_READ事件,則進行讀取和打印
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(128);
int len = socketChannel.read(byteBuffer);
// 如果有數據,把數據打印出來
if (len > 0) {
System.out.println("接收到消息:" + new String(byteBuffer.array()));
} else if (len == -1) { // 如果客戶端斷開連接,關閉Socket
System.out.println("客戶端斷開連接");
socketChannel.close();
}
}
//從事件集合里刪除本次處理的key,防止下次select重復處理
iterator.remove();
}
}
}
}
啟動以上代碼,然后開啟多個客戶端,我們證明了:
1、一個線程就可以處理多個客戶端;
2、只處理有連接事件的客戶端,不會有多余的客戶端進行阻塞
3、只處理有數據的客戶端,提高性能,防止全部遍歷,只把有數據的客戶端存入到就緒列表中

本人工作3年中級菜鳥程序員, 最近想回顧一下知識,做了一些簡單總結同時也為了自己今后復習方便,如果有邏輯錯誤,大家體諒,同時也希望大牛們能給出正確答案讓我改正,謝謝!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM