国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

BIO、NIO、IO多路復(fù)用模型詳細介紹&Java NIO 網(wǎng)絡(luò)編程

這篇具有很好參考價值的文章主要介紹了BIO、NIO、IO多路復(fù)用模型詳細介紹&Java NIO 網(wǎng)絡(luò)編程。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

前言

上文介紹了網(wǎng)絡(luò)編程的基礎(chǔ)知識,并基于 Java 編寫了 BIO 的網(wǎng)絡(luò)編程。我們知道 BIO 模型是存在巨大問題的,比如 C10K 問題,其本質(zhì)就是因其阻塞原因,導(dǎo)致如果想要承受更多的請求就必須有足夠多的線程,但是足夠多的線程會帶來內(nèi)存占用問題、CPU上下文切換帶來的性能問題,從而造成服務(wù)端崩潰的現(xiàn)象。怎么解決這一問題呢?優(yōu)化唄,所以后面就有了NIO、AIO、IO多路復(fù)用。本文將對這幾個模型詳細說明并基于 Java 編寫 NIO。

基本概念

I/O阻塞是哪里阻塞、怎么阻塞?先簡單了解一些基本概念

  • 用戶空間:被分配給用戶進程的虛擬地址空間,用來存儲用戶進程的代碼、數(shù)據(jù)和堆棧等。
  • 內(nèi)核空間:操作系統(tǒng)的基礎(chǔ),負責(zé)管理計算機的硬件資源和提供系統(tǒng)調(diào)用接口,同時也是用戶空間和硬件之間的橋梁。

BIO、NIO、IO多路復(fù)用模型詳細介紹&Java NIO 網(wǎng)絡(luò)編程,Java,nio,java,網(wǎng)絡(luò)

為了保證操作系統(tǒng)的安全性和穩(wěn)定性,用戶進程和操作系統(tǒng)內(nèi)核是隔離的,用戶進程不能直接訪問內(nèi)核空間,而是需要通過系統(tǒng)調(diào)用等方式向內(nèi)核發(fā)起請求,由內(nèi)核代表用戶進程執(zhí)行操作。

也就是說我們的應(yīng)用程序在向硬件設(shè)備,比如網(wǎng)卡、磁盤等讀取或?qū)懭霐?shù)據(jù)時需要經(jīng)過內(nèi)核。下面對BIO、NIO、IO多路復(fù)用模型逐一介紹,詳細了解各模型IO過程。

BIO過程

首先明確一下,我們所說的IO阻塞是用戶進程也就是用戶空間中的程序在向硬件設(shè)備讀取的這個過程,在還沒有數(shù)據(jù)時給用戶的反映是需要一直等待的,這個我們叫阻塞IO。過程如下圖:
BIO、NIO、IO多路復(fù)用模型詳細介紹&Java NIO 網(wǎng)絡(luò)編程,Java,nio,java,網(wǎng)絡(luò)

我們可以看到,在進程向內(nèi)核發(fā)起調(diào)用后直到數(shù)據(jù)返回,整個過程都是阻塞的,結(jié)合Java BIO 編程,也就是說在 inputStream.read() 這個過程是阻塞的,就存在幾個問題:

  1. 由于阻塞會占用當(dāng)前線程,使之不能進行其他操作,當(dāng)有新的請求時只能新建線程。在 Linux 系統(tǒng)中,每個線程的默認棧大小為 8MB,在不考慮其他因素的情況下,一個 8G 的服務(wù)器最多也就承載1000個請求量。
  2. 由于線程數(shù)會隨著請求量增大而增大,當(dāng)有大量的線程阻塞喚醒,CPU 頻繁的切換上下文會導(dǎo)致性能的下降。

這個問題也就是C10K問題的本質(zhì),看上去很直觀,使用少的線程就處理多個IO是不是就可以解決呢?繼續(xù)看NIO過程。

NIO過程

NIO我們說的是非阻塞,通過對BIO的說明,NIO的非阻塞體現(xiàn)在:無論有無數(shù)據(jù)都直接響應(yīng)給用戶進程,如下圖:

BIO、NIO、IO多路復(fù)用模型詳細介紹&Java NIO 網(wǎng)絡(luò)編程,Java,nio,java,網(wǎng)絡(luò)

我們可以看到,確實是在用戶進程調(diào)用recvfrom()函數(shù)后直接響應(yīng),但是在沒有拿到數(shù)據(jù)之前一直在輪詢調(diào)用,雖然沒有因為阻塞造成CPU上下文的切換,但是CPU一直處于空轉(zhuǎn)狀態(tài),不能充分發(fā)揮CPU的作用。與BIO一樣,在單線程的情況下,只能依次處理IO事件,單個線程依然處理不了多個IO事件。

IO多路復(fù)用過程

既然NIO與BIO一樣并不能解決因阻塞可能會造成的C10K問題,那如何讓一個線程可以處理多個IO事件呢?可不可以這樣:用一個線程專門監(jiān)聽這些IO,一旦哪個IO有數(shù)據(jù)了再去接收數(shù)據(jù)。IO多路復(fù)用就是這個原理,如下圖:

BIO、NIO、IO多路復(fù)用模型詳細介紹&Java NIO 網(wǎng)絡(luò)編程,Java,nio,java,網(wǎng)絡(luò)

我們可以看到,多了一個select()函數(shù)調(diào)用,select()會去監(jiān)聽指定的FD(這里注意一下,在Linux中,一切皆文件,包括socket),內(nèi)核去監(jiān)聽FD對應(yīng)的sockets。任意一個或多個socket有數(shù)據(jù)了就返回給select(),這個時候再去調(diào)用recvfrom()接收sockets的數(shù)據(jù),從而實現(xiàn)了單個線程處理多個I/O操作,提高系統(tǒng)的效率和性能。

在Linux下,常用的I/O多路復(fù)用方式有三種:select、poll和epoll。

  • select和poll的原理是基于輪詢,即不斷地查詢所有注冊的I/O事件,如果有事件發(fā)生就立即通知應(yīng)用程序。這種方式的效率較低,因為每次查詢都需要遍歷所有的I/O事件。

  • epoll的原理是基于事件通知,即只有當(dāng)I/O事件發(fā)生時,才會通知應(yīng)用程序。這種方式的效率更高,因為它避免了無效的查詢。

Java NIO編程

相比Java BIO編程,Java NIO編程理解起來沒有那么直觀,不過在理解多個IO模型(尤其是IO多路復(fù)用)后就相對容易理解了,Java NIO實際上就是IO多路復(fù)用。

Java NIO 核心概念

在Java NIO編程中,有幾個核心的概念(組件)需要了解:

  • 通道(Channel):通道是對原始I/O操作的抽象,可以用于讀取和寫入數(shù)據(jù)。它可以與文件、套接字等進行交互。

  • 緩沖區(qū)(Buffer):緩沖區(qū)是一個容器,用于存儲數(shù)據(jù)。在進行讀寫操作時,數(shù)據(jù)會先被讀取到緩沖區(qū)中,然后從緩沖區(qū)中寫入或讀取。

  • 選擇器(Selector):選擇器是Java NIO提供的一種多路復(fù)用機制,可以通過一個線程管理多個通道的I/O操作。

相對于BIO,開發(fā)者不直接與Socket交互,而是通過Selector與多個Channel交互,同時Buffer提供了方法來管理緩沖區(qū)的容量、位置和限制,通過設(shè)置這些屬性,可以控制數(shù)據(jù)的讀寫位置和范圍??傊甆IO在提升IO處理效率和性能的同時支持更豐富的功能,

Java NIO 示例

以下是一個簡單的Java NIO網(wǎng)絡(luò)編程示例,用于創(chuàng)建一個基于NIO的服務(wù)器和客戶端:

服務(wù)端代碼:

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;

public class NIOServer {

    private Selector selector;

    public static void main(String[] args) throws IOException {
        NIOServer server = new NIOServer();
        server.startServer();
    }

    public void startServer() throws IOException {
        // 創(chuàng)建Selector
        selector = Selector.open();

        // 創(chuàng)建ServerSocketChannel,并綁定端口
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);
        serverChannel.socket().bind(new InetSocketAddress(8888));

        // 將ServerSocketChannel注冊到Selector上,并監(jiān)聽連接事件。當(dāng)接收到一個客戶端連接請求時就緒。該操作只給服務(wù)器使用。
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("Server started on port 8888");

        // 循環(huán)等待事件發(fā)生
        while (true) {
            // 等待事件觸發(fā),阻塞 | selectNow():非阻塞,立刻返回。
            selector.select();

            Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
            while (keys.hasNext()) {
                SelectionKey key = keys.next();
                // 移除當(dāng)前處理的SelectionKey
                keys.remove();

                if (key.isAcceptable()) {
                    // 處理連接請求
                    handleAccept(key);
                }

                if (key.isReadable()) {
                    // 處理讀數(shù)據(jù)請求
                    handleRead(key);
                }
            }
        }
    }

    private void handleAccept(SelectionKey key) throws IOException {
        ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
        // 監(jiān)聽到ServerSocketChannel連接事件,獲取到連接的客戶端
        SocketChannel clientChannel = serverChannel.accept();
        clientChannel.configureBlocking(false);
        // 將clientChannel注冊到Selector上,并監(jiān)聽讀事件,當(dāng)操作系統(tǒng)讀緩沖區(qū)有數(shù)據(jù)可讀時就緒(該客戶端的)。
        clientChannel.register(selector, SelectionKey.OP_READ);

        System.out.println("Client connected: " + clientChannel.getRemoteAddress());
    }

    private void handleRead(SelectionKey key) throws IOException {
        SocketChannel clientChannel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int bytesRead = clientChannel.read(buffer);

        if (bytesRead == -1) {
            // 客戶端斷開連接
            key.cancel();
            clientChannel.close();
            System.out.println("Client disconnected ");
            return;
        }

        byte[] data = new byte[bytesRead];
        buffer.flip();
        buffer.get(data);

        String message = new String(data).trim();
        System.out.println("Received message from client: " + message);

        // 回復(fù)客戶端
        String response = "Server response: " + message;
        ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
        clientChannel.write(responseBuffer);
    }
}

客戶端代碼:

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

public class NIOClient {
    private Selector selector;
    private SocketChannel socketChannel;

    public static void main(String[] args) {
        NIOClient client = new NIOClient();
        new Thread(() -> client.doConnect("localhost", 8888)).start();
        Scanner scanner = new Scanner(System.in);
        while (true) {
            String message = scanner.nextLine();
            if ("bye".equals(message)) {
                // 如果發(fā)送的消息是"bye",則關(guān)閉連接并退出循環(huán)
                client.doDisConnect();
                break;
            }
            client.sendMsg(message);
        }

    }

    private void doDisConnect() {
        try {
            socketChannel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void sendMsg(String message) {
        // 發(fā)送消息到服務(wù)器
        ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
        try {
            socketChannel.write(buffer);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void doConnect(String host, int port) {
        try {
            selector = Selector.open();
            // 創(chuàng)建SocketChannel并連接服務(wù)器
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress(host, port));
            // 等待連接完成
            while (!socketChannel.finishConnect()) {
                // 連接未完成,可以做一些其他的事情
            }
            socketChannel.register(selector, SelectionKey.OP_READ);
            System.out.println("連接成功!");
            while (true) {
                // 等待事件觸發(fā),阻塞 | selectNow():非阻塞,立刻返回。
                selector.select();

                Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
                while (keys.hasNext()) {
                    SelectionKey key = keys.next();
                    // 移除當(dāng)前處理的SelectionKey
                    keys.remove();
                    if (key.isReadable()) {
                        // 處理讀數(shù)據(jù)請求
                        handleRead(key);
                    }
                }
            }
        } catch (IOException e) {
            System.out.println("連接失?。。?!");
            e.printStackTrace();
        }
    }

    private void handleRead(SelectionKey key) throws IOException {
        SocketChannel clientChannel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int bytesRead = clientChannel.read(buffer);

        if (bytesRead == -1) {
            // 釋放資源
            key.cancel();
            clientChannel.close();
            return;
        }

        byte[] data = new byte[bytesRead];
        buffer.flip();
        buffer.get(data);

        String message = new String(data).trim();
        System.out.println("Received message from server: " + message);
    }


}

總結(jié)

通過本文介紹,我們可以了解各個IO模型原理,并且對很多概念有了更清晰的認識,比如:
阻塞體現(xiàn)在:用戶進程發(fā)起系統(tǒng)調(diào)用接口后,不論有無數(shù)據(jù),是否直接響應(yīng)結(jié)果?如果直接響應(yīng)就是非阻塞,等待就是阻塞;
IO多路復(fù)用原理就是單個線程處理多個I/O操作,從而提高系統(tǒng)的效率和性能;
并且通過對IO多路復(fù)用的理解快速的入門了Java NIO 編程。文章來源地址http://www.zghlxwxcb.cn/news/detail-622929.html

到了這里,關(guān)于BIO、NIO、IO多路復(fù)用模型詳細介紹&Java NIO 網(wǎng)絡(luò)編程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • BIO到NIO、多路復(fù)用器, 從理論到實踐, 結(jié)合實際案例對比各自效率與特點(下)

    BIO到NIO、多路復(fù)用器, 從理論到實踐, 結(jié)合實際案例對比各自效率與特點(下)

    本篇文章是BIO到NIO、多路復(fù)用器, 從理論到實踐, 結(jié)合實際案例對比各自效率與特點(上)的下一篇, 如果沒有看的小伙伴, 可以先看下, 不然可能會不連貫. 多路復(fù)用器是對于傳統(tǒng)NIO的優(yōu)化, 解決了傳統(tǒng)NIO無法直接獲取所有所有連接的狀態(tài), 需要挨個遍歷所有連接查看是否準(zhǔn)備就緒

    2024年02月10日
    瀏覽(49)
  • 【網(wǎng)絡(luò)編程】IO多路復(fù)用

    【網(wǎng)絡(luò)編程】IO多路復(fù)用

    ????????IO多路復(fù)用是一種高效的I/O處理方式,它 允許單個進程能夠同時監(jiān)視多個文件描述符 (sockets、文件等),并在其中任何一個文件描述符準(zhǔn)備好進行I/O操作時進行處理。它的核心在于 使用少量的線程或進程來管理多個I/O操作,以提高系統(tǒng)的性能和響應(yīng)速度 。 1.

    2024年02月09日
    瀏覽(25)
  • 網(wǎng)絡(luò)編程 IO多路復(fù)用 [epoll版] (TCP網(wǎng)絡(luò)聊天室)

    網(wǎng)絡(luò)編程 IO多路復(fù)用 [epoll版] (TCP網(wǎng)絡(luò)聊天室)

    //head.h? ? ? ? ? ? 頭文件 //TcpGrpSer.c? ? ?服務(wù)器端 //TcpGrpUsr.c? ? ?客戶端 通過IO多路復(fù)用實現(xiàn)服務(wù)器在單進程單線程下可以與多個客戶端交互 ?API epoll函數(shù) ?head.h TcpGrpSer.c TcpGrpUsr.c ?

    2024年02月11日
    瀏覽(24)
  • 網(wǎng)絡(luò)編程 IO多路復(fù)用 [select版] (TCP網(wǎng)絡(luò)聊天室)

    網(wǎng)絡(luò)編程 IO多路復(fù)用 [select版] (TCP網(wǎng)絡(luò)聊天室)

    //head.h? ? ? ? ? ? ? ? ?頭文件 //TcpGrpSer.c? ? ? ? 服務(wù)器端 //TcpGrpUsr.c? ? ? ? 客戶端 select函數(shù)? 功能:阻塞函數(shù),讓內(nèi)核去監(jiān)測集合中的文件描述符是否準(zhǔn)備就緒,若準(zhǔn)備就緒則解除阻塞。 原型: head.h TcpGrpSer.c TcpGrpUsr.c ? ?

    2024年02月14日
    瀏覽(25)
  • 【Java基礎(chǔ)】BIO/NIO/AIO的詳細介紹與比較區(qū)分

    【Java基礎(chǔ)】BIO/NIO/AIO的詳細介紹與比較區(qū)分

    BIO 全稱 Blocking I/O,它是 JDK 1.4 之前的傳統(tǒng)IO模型,是一種同步阻塞的IO,線程發(fā)起 IO 后,一直阻塞,直到緩沖區(qū)數(shù)據(jù)就緒后,在進入下一步操作 BIO存在的問題: 無法應(yīng)對高并發(fā)的場景 連接建立后,當(dāng)前線程沒有數(shù)據(jù)可讀就會阻塞,造成資源浪費 BIO適用場景: 客戶端連接數(shù)

    2024年01月20日
    瀏覽(26)
  • 【Linux網(wǎng)絡(luò)編程】TCP并發(fā)服務(wù)器的實現(xiàn)(IO多路復(fù)用select)

    【Linux網(wǎng)絡(luò)編程】TCP并發(fā)服務(wù)器的實現(xiàn)(IO多路復(fù)用select)

    服務(wù)器模型主要分為兩種, 循環(huán)服務(wù)器 和 并發(fā)服務(wù)器 。 循環(huán)服務(wù)器 : 在同一時間只能處理一個客戶端的請求。 并發(fā)服務(wù)器 : 在同一時間內(nèi)能同時處理多個客戶端的請求。 TCP的服務(wù)器默認的就是一個循環(huán)服務(wù)器,原因是有兩個阻塞 accept函數(shù) 和recv函數(shù) 之間會相互影響。

    2024年02月03日
    瀏覽(100)
  • Java網(wǎng)絡(luò)編程-深入理解BIO、NIO

    Java網(wǎng)絡(luò)編程-深入理解BIO、NIO

    BIO BIO 為 Blocked-IO(阻塞 IO),在 JDK1.4 之前建立網(wǎng)絡(luò)連接時,只能使用 BIO 使用 BIO 時,服務(wù)端會對客戶端的每個請求都建立一個線程進行處理,客戶端向服務(wù)端發(fā)送請求后,先咨詢服務(wù)端是否有線程響應(yīng),如果沒有就會等待或者被拒絕 BIO 基本使用代碼: 服務(wù)端: 客戶端:

    2024年02月04日
    瀏覽(24)
  • 網(wǎng)絡(luò)模型與 IO 多路復(fù)用

    網(wǎng)絡(luò)模型與 IO 多路復(fù)用

    ??socket也稱作“套接字”,用于描述IP地址和端口,是一個通信鏈路的描述符。應(yīng)用程序通常通過“套接字”向?qū)Χ税l(fā)出請求或者應(yīng)答網(wǎng)絡(luò)請求。 ??socket是連接運行在網(wǎng)絡(luò)上的兩個程序之間的通信端點。通信的兩端都有socket,它是一個通道,數(shù)據(jù)在兩個socket之間進行傳輸

    2024年02月01日
    瀏覽(28)
  • 【Netty專題】【網(wǎng)絡(luò)編程】從OSI、TCP/IP網(wǎng)絡(luò)模型開始到BIO、NIO(Netty前置知識)

    【Netty專題】【網(wǎng)絡(luò)編程】從OSI、TCP/IP網(wǎng)絡(luò)模型開始到BIO、NIO(Netty前置知識)

    我是有點怕網(wǎng)絡(luò)編程的,總有點【談網(wǎng)色變】的感覺。為了讓自己不再【談網(wǎng)色變】,所以我想過系統(tǒng)學(xué)習(xí)一下,然后再做個筆記這樣,加深一下理解。但是真要系統(tǒng)學(xué)習(xí),其實還是要花費不少時間的,所以這里也只是簡單的,盡可能地覆蓋一下,梳理一些我認為比較迫切需

    2024年02月06日
    瀏覽(28)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包