日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java 网络 io流_【015期】JavaSE面试题(十五):网络IO流

發布時間:2025/3/21 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 网络 io流_【015期】JavaSE面试题(十五):网络IO流 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

什么是bio

同步阻塞式IO,服務端創建一個ServerSocket,然后客戶端用一個Socket去連接那個ServerSocket,然后ServerSocket接收到一個Socket的連接請求就創建一個Socket和一個線程去跟那個Socket進行通信。

public class BioServer {

public static void main(String[] args) {

// 服務端開啟一個端口進行監聽

int port = 8080;

ServerSocket serverSocket = null; //服務端

Socket socket; //客戶端

InputStream in = null;

OutputStream out = null;

try {

serverSocket = new ServerSocket(port); //通過構造函數創建ServerSocket,指定監聽端口,如果端口合法且空閑,服務器就會監聽成功

// 通過無限循環監聽客戶端連接,如果沒有客戶端接入,則會阻塞在accept操作

while (true) {

System.out.println("Waiting for a new Socket to establish" + " ," + new Date().toString());

socket = serverSocket.accept();//阻塞 三次握手

in = socket.getInputStream();

byte[] buffer = new byte[1024];

int length = 0;

while ((length = in.read(buffer)) > 0) {//阻塞

System.out.println("input is:" + new String(buffer, 0, length) + " ," + new Date().toString());

out = socket.getOutputStream();

out.write("success".getBytes());

System.out.println("Server end" + " ," + new Date().toString());

}

}

} catch (Exception e) {

e.printStackTrace();

} finally {

// 必要的清理活動

if (serverSocket != null) {

try {

serverSocket.close();

} catch (IOException e) {

e.printStackTrace();

}

}

if (in != null) {

try {

in.close();

} catch (IOException e) {

e.printStackTrace();

}

}

if (out != null) {

try {

out.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

}

什么是nio

同步非阻塞

包括Selector,這是多路復用器,selector會不斷輪詢注冊的channel,如果某個channel上發生了讀寫事件,selector就會將這些channel獲取出來,我們通過SelectionKey獲取有讀寫事件的channel,就可以進行IO操作。一個Selector就通過一個線程,就可以輪詢成千上萬的channel,這就意味著你的服務端可以接入成千上萬的客戶端。

public class NioDemo implements Runnable {

public int id = 100001;

public int bufferSize = 2048;

@Override

public void run() {

init();

}

public void init() {

try {

// 創建通道和選擇器

ServerSocketChannel socketChannel = ServerSocketChannel.open();

Selector selector = Selector.open();

InetSocketAddress inetSocketAddress = new InetSocketAddress(

InetAddress.getLocalHost(), 4700);

socketChannel.socket().bind(inetSocketAddress);

// 設置通道非阻塞 綁定選擇器

socketChannel.configureBlocking(false);

socketChannel.register(selector, SelectionKey.OP_ACCEPT).attach(

id++);

System.out.println("Server started .... port:4700");

listener(selector);

} catch (Exception e) {

}

}

public void listener(Selector in_selector) {

try {

while (true) {

Thread.sleep(1 * 1000);

in_selector.select(); // 阻塞 直到有就緒事件為止

Set readySelectionKey = in_selector

.selectedKeys();

Iterator it = readySelectionKey.iterator();

while (it.hasNext()) {

SelectionKey selectionKey = it.next();

// 判斷是哪個事件

if (selectionKey.isAcceptable()) {// 客戶請求連接

System.out.println(selectionKey.attachment()

+ " - 接受請求事件");

// 獲取通道 接受連接,

// 設置非阻塞模式(必須),同時需要注冊 讀寫數據的事件,這樣有消息觸發時才能捕獲

ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey

.channel();

serverSocketChannel

.accept()

.configureBlocking(false)

.register(

in_selector,

SelectionKey.OP_READ

| SelectionKey.OP_WRITE).attach(id++);

System.out

.println(selectionKey.attachment() + " - 已連接");

// 下面這種寫法是有問題的 不應該在serverSocketChannel上面注冊

/*

* serverSocketChannel.configureBlocking(false);

* serverSocketChannel.register(in_selector,

* SelectionKey.OP_READ);

* serverSocketChannel.register(in_selector,

* SelectionKey.OP_WRITE);

*/

}

if (selectionKey.isReadable()) {// 讀數據

System.out.println(selectionKey.attachment()

+ " - 讀數據事件");

SocketChannel clientChannel = (SocketChannel) selectionKey.channel();

ByteBuffer receiveBuf = ByteBuffer.allocate(bufferSize);

clientChannel.read(receiveBuf);

System.out.println(selectionKey.attachment()

+ " - 讀取數據:" + getString(receiveBuf));

}

if (selectionKey.isWritable()) {// 寫數據

System.out.println(selectionKey.attachment()

+ " - 寫數據事件");

SocketChannel clientChannel = (SocketChannel) selectionKey.channel();

ByteBuffer sendBuf = ByteBuffer.allocate(bufferSize);

String sendText = "hello\n";

sendBuf.put(sendText.getBytes());

sendBuf.flip(); //寫完數據后調用此方法

clientChannel.write(sendBuf);

}

if (selectionKey.isConnectable()) {

System.out.println(selectionKey.attachment()

+ " - 連接事件");

}

// 必須removed 否則會繼續存在,下一次循環還會進來,

// 注意removed 的位置,針對一個.next() remove一次

it.remove();

}

}

} catch (Exception e) {

System.out.println("Error - " + e.getMessage());

e.printStackTrace();

}

}

/**

* ByteBuffer 轉換 String

*

* @param buffer

* @return

*/

public static String getString(ByteBuffer buffer) {

String string = "";

try {

for (int i = 0; i < buffer.position(); i++) {

string += (char) buffer.get(i);

}

return string;

} catch (Exception ex) {

ex.printStackTrace();

return "";

}

}

}

什么是aio

異步非阻塞

每個連接發送過來的請求,都會綁定一個buffer,然后通知操作系統去異步完成讀,此時你的程序是會去干別的事兒的,等操作系統完成數據讀取之后,就會回調你的接口,給你操作系統異步讀完的數據。

public class AIOServer {

public final static int PORT = 9888;

private AsynchronousServerSocketChannel server;

public AIOServer() throws IOException {

server = AsynchronousServerSocketChannel.open().bind(

new InetSocketAddress(PORT));

}

public void startWithFuture() throws InterruptedException,

ExecutionException, TimeoutException {

while (true) {// 循環接收客戶端請求

Future future = server.accept();

AsynchronousSocketChannel socket = future.get();// get() 是為了確保 accept 到一個連接

handleWithFuture(socket);

}

}

public void handleWithFuture(AsynchronousSocketChannel channel) throws InterruptedException, ExecutionException, TimeoutException {

ByteBuffer readBuf = ByteBuffer.allocate(2);

readBuf.clear();

while (true) {// 一次可能讀不完

//get 是為了確保 read 完成,超時時間可以有效避免DOS攻擊,如果客戶端一直不發送數據,則進行超時處理

Integer integer = channel.read(readBuf).get(10, TimeUnit.SECONDS);

System.out.println("read: " + integer);

if (integer == -1) {

break;

}

readBuf.flip();

System.out.println("received: " + Charset.forName("UTF-8").decode(readBuf));

readBuf.clear();

}

}

public void startWithCompletionHandler() throws InterruptedException,

ExecutionException, TimeoutException {

server.accept(null,

new CompletionHandler() {

public void completed(AsynchronousSocketChannel result, Object attachment) {

server.accept(null, this);// 再此接收客戶端連接

handleWithCompletionHandler(result);

}

@Override

public void failed(Throwable exc, Object attachment) {

exc.printStackTrace();

}

});

}

public void handleWithCompletionHandler(final AsynchronousSocketChannel channel) {

try {

final ByteBuffer buffer = ByteBuffer.allocate(4);

final long timeout = 10L;

channel.read(buffer, timeout, TimeUnit.SECONDS, null, new CompletionHandler() {

@Override

public void completed(Integer result, Object attachment) {

System.out.println("read:" + result);

if (result == -1) {

try {

channel.close();

} catch (IOException e) {

e.printStackTrace();

}

return;

}

buffer.flip();

System.out.println("received message:" + Charset.forName("UTF-8").decode(buffer));

buffer.clear();

channel.read(buffer, timeout, TimeUnit.SECONDS, null, this);

}

@Override

public void failed(Throwable exc, Object attachment) {

exc.printStackTrace();

}

});

} catch (Exception e) {

e.printStackTrace();

}

}

public static void main(String args[]) throws Exception {

// new AIOServer().startWithFuture();

new AIOServer().startWithCompletionHandler();

Thread.sleep(100000);

}

}

什么是epoll

一種多路復用的技術,可以解決之前poll和select大量并發連接情況下cpu利用率過高,以及需要遍歷整個被偵聽的描述符集的問題。epoll只要遍歷那些被內核IO事件異步喚醒而加入Ready隊列的描述符集合就行了。

什么是mmap技術

把一個磁盤文件映射到內存里來,然后把映射到內存里來的數據通過socket發送出去

有一種mmap技術,也就是內存映射,直接將磁盤文件數據映射到內核緩沖區,這個映射的過程是基于DMA引擎拷貝的,同時用戶緩 沖區是跟內核緩沖區共享一塊映射數據的,建立共享映射之后,就不需要從內核緩沖區拷貝到用戶緩沖區了

光是這一點,就可以避免一次拷貝了,但是這個過程中還是會用戶態切換到內核態去進行映射拷貝,接著再次從內核態切換到用戶態, 建立用戶緩沖區和內核緩沖區的映射

接著把數據通過Socket發送出去,還是要再次切換到內核態

接著直接把內核緩沖區里的數據拷貝到Socket緩沖區里去,然后再拷貝到網絡協議引擎里,發送出去就可以了,最后切換回用戶態

減少一次拷貝,但是并不減少切換次數,一共是4次切換,3次拷貝

什么是零拷貝技術

linux提供了sendfile,也就是零拷貝技術

這個零拷貝技術,就是先從用戶態切換到內核態,在內核態的狀態下,把磁盤上的數據拷貝到內核緩沖區,同時從內核緩沖區拷貝一些 offset和length到Socket緩沖區;接著從內核態切換到用戶態,從內核緩沖區直接把數據拷貝到網絡協議引擎里去

同時從Socket緩沖區里拷貝一些offset和length到網絡協議引擎里去,但是這個offset和length的量很少,幾乎可以忽略

只要2次切換,2次拷貝,就可以了

說一下select,poll,epoll的區別?

select,poll實現需要自己不斷輪詢所有fd集合,直到設備就緒,期間可能要睡眠和喚醒多次交替。

epoll也需要調用epoll_wait不斷輪詢就緒鏈表,期間也可能多次睡眠和喚醒交替,但是它是設備就緒時,調用回調函數,把就緒fd放入就緒鏈表中,并喚醒在epoll_wait中進入睡眠的進程。雖然都要睡眠和交替,但是select和poll在“醒著”的時候要遍歷整個fd集合,而epoll在“醒著”的時候只要判斷一下就緒鏈表是否為空就行了,這節省了大量的CPU時間。這就是回調機制帶來的性能提升。

select,poll每次調用都要把fd集合從用戶態往內核態拷貝一次,并且要把current往設備等待隊列中掛一次,而epoll只要一次拷貝,而且把current往等待隊列上掛也只掛一次(在epoll_wait的開始,注意這里的等待隊列并不是設備等待隊列,只是一個epoll內部定義的等待隊列)。這也能節省不少的開銷。

總結

以上是生活随笔為你收集整理的java 网络 io流_【015期】JavaSE面试题(十五):网络IO流的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。