大學(xué)生網(wǎng)站規(guī)劃建設(shè)企業(yè)網(wǎng)站托管
IO模型
用什么樣的通道進(jìn)行數(shù)據(jù)傳輸和接收,java支持3種io網(wǎng)絡(luò)編程模式 BIO NIO AIO
BIO
同步阻塞 一個客戶端連接對應(yīng)一個處理線程
BIO示例代碼(客戶端和服務(wù)端)
package com.tuling.bio;import java.io.IOException;
import java.net.Socket;public class SocketClient {public static void main(String[] args) throws IOException, InterruptedException {Socket socket = new Socket("127.0.0.1", 9000);//向服務(wù)端發(fā)送數(shù)據(jù)socket.getOutputStream().write("HelloServer".getBytes());socket.getOutputStream().flush();System.out.println("向服務(wù)端發(fā)送數(shù)據(jù)結(jié)束");byte[] bytes = new byte[1024];//接收服務(wù)端回傳的數(shù)據(jù)socket.getInputStream().read(bytes);System.out.println("接收到服務(wù)端的數(shù)據(jù):" + new String(bytes));socket.close();}
}
package com.tuling.bio;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("有客戶端連接了。。");new Thread(new Runnable() {@Overridepublic void run() {try {handler(clientSocket);} catch (IOException e) {e.printStackTrace();}}}).start();}}private static void handler(Socket clientSocket) throws IOException {byte[] bytes = new byte[1024];System.out.println("準(zhǔn)備read。。");//接收客戶端的數(shù)據(jù),阻塞方法,沒有數(shù)據(jù)可讀時就阻塞int read = clientSocket.getInputStream().read(bytes);System.out.println("read完畢。。");if (read != -1) {System.out.println("接收到客戶端的數(shù)據(jù):" + new String(bytes, 0, read));}clientSocket.getOutputStream().write("HelloClient".getBytes());clientSocket.getOutputStream().flush();}
}
先啟動服務(wù)端,再啟動客戶端,客戶端和服務(wù)端直接可以互發(fā)消息
缺點:io代碼里read操作是阻塞操作,如果連接不做數(shù)據(jù)讀寫操作會導(dǎo)致線程阻塞,浪費資源
如果線程很多,會導(dǎo)致服務(wù)器線程太多,壓力太大
應(yīng)用場景:連接數(shù)目小且固定的架構(gòu),對服務(wù)器資源要求比較高
NIO
同步非阻塞 一個線程可以處理多個請求。 客戶端發(fā)送的請求可以注冊到多路復(fù)用selector上,多路復(fù)用查詢到IO請求就進(jìn)行處理
應(yīng)用場景 連接多且連接比較短的架構(gòu),如聊天服務(wù)器,編程比較復(fù)雜
nio示例代碼
package com.tuling.nio;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class NioServer {// 保存客戶端連接static List<SocketChannel> channelList = new ArrayList<>();public static void main(String[] args) throws IOException {// 創(chuàng)建NIO ServerSocketChannel,與BIO的serverSocket類似ServerSocketChannel serverSocket = ServerSocketChannel.open();serverSocket.socket().bind(new InetSocketAddress(9000));// 設(shè)置ServerSocketChannel為非阻塞serverSocket.configureBlocking(false);System.out.println("服務(wù)啟動成功");while (true) {// 非阻塞模式accept方法不會阻塞,否則會阻塞// NIO的非阻塞是由操作系統(tǒng)內(nèi)部實現(xiàn)的,底層調(diào)用了linux內(nèi)核的accept函數(shù)SocketChannel socketChannel = serverSocket.accept();if (socketChannel != null) { // 如果有客戶端進(jìn)行連接System.out.println("連接成功");// 設(shè)置SocketChannel為非阻塞socketChannel.configureBlocking(false);// 保存客戶端連接在List中channelList.add(socketChannel);}// 遍歷連接進(jìn)行數(shù)據(jù)讀取Iterator<SocketChannel> iterator = channelList.iterator();while (iterator.hasNext()) {SocketChannel sc = iterator.next();ByteBuffer byteBuffer = ByteBuffer.allocate(128);// 非阻塞模式read方法不會阻塞,否則會阻塞int len = sc.read(byteBuffer);// 如果有數(shù)據(jù),把數(shù)據(jù)打印出來if (len > 0) {System.out.println("接收到消息:" + new String(byteBuffer.array()));} else if (len == -1) { // 如果客戶端斷開,把socket從集合中去掉iterator.remove();System.out.println("客戶端斷開連接");}}}}
}
nio引入多路復(fù)用器示例代碼
package com.tuling.nio;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 {// 創(chuàng)建NIO ServerSocketChannelServerSocketChannel serverSocket = ServerSocketChannel.open();serverSocket.socket().bind(new InetSocketAddress(9000));// 設(shè)置ServerSocketChannel為非阻塞serverSocket.configureBlocking(false);// 打開Selector處理Channel,即創(chuàng)建epollSelector selector = Selector.open();// 把ServerSocketChannel注冊到selector上,并且selector對客戶端accept連接操作感興趣SelectionKey selectionKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服務(wù)啟動成功");while (true) {// 阻塞等待需要處理的事件發(fā)生selector.select();// 獲取selector中注冊的全部事件的 SelectionKey 實例Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();// 遍歷SelectionKey對事件進(jìn)行處理while (iterator.hasNext()) {SelectionKey key = iterator.next();// 如果是OP_ACCEPT事件,則進(jìn)行連接獲取和事件注冊if (key.isAcceptable()) {ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel socketChannel = server.accept();socketChannel.configureBlocking(false);// 這里只注冊了讀事件,如果需要給客戶端發(fā)送數(shù)據(jù)可以注冊寫事件SelectionKey selKey = socketChannel.register(selector, SelectionKey.OP_READ);System.out.println("客戶端連接成功");} else if (key.isReadable()) { // 如果是OP_READ事件,則進(jìn)行讀取和打印SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer byteBuffer = ByteBuffer.allocateDirect(128);int len = socketChannel.read(byteBuffer);// 如果有數(shù)據(jù),把數(shù)據(jù)打印出來if (len > 0) {System.out.println("接收到消息:" + new String(byteBuffer.array()));} else if (len == -1) { // 如果客戶端斷開連接,關(guān)閉SocketSystem.out.println("客戶端斷開連接");socketChannel.close();}}//從事件集合里刪除本次處理的key,防止下次select重復(fù)處理iterator.remove();}}}
}
NIO三大核心組件: channel buffer selector
channel 通道 ,底層數(shù)組
buffer 緩沖
nio底層在jdk1.4版本基于linux內(nèi)核函數(shù)select()或poll()來實現(xiàn),類似于nioserver代碼,每次都會輪詢所有的channel??茨膫€有讀取事件即進(jìn)行處理,沒有繼續(xù)遍歷。 jdk1.5后引入epoll基于事件相應(yīng)來優(yōu)化nio
AIO 異步非阻塞 一般用于連接數(shù)多且連接時間較長的應(yīng)用 jdk7支持
為什么netty 使用NIO而不是AIO
在linux系統(tǒng),aio底層仍然使用epoll,沒有很好實現(xiàn)AIO,性能沒有明顯優(yōu)勢,而且被jdk封裝不易優(yōu)化。
netty是異步非阻塞框架,對于aio做了很多異步封裝