<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    【死磕 NIO】— ServerSocketChannel 到底有什么缺陷?

    共 3284字,需瀏覽 7分鐘

     ·

    2022-05-24 13:59


    大家好,我是大明哥,一個(gè)專注于【死磕 Java】的程序員。?

    【死磕 Java 】系列為作者「chenssy」 傾情打造的 Java 系列文章,深入分析 Java 相關(guān)技術(shù)核心原理及源碼。?

    死磕 Java :https://www.cmsblogs.com/group/1420041599311810560

    上篇文章大明哥介紹了 SocketChannel 的核心原理及其源碼,這篇文章就來介紹如何使用 ServerSocketChannel,分析單獨(dú)使用 ServerSocketChannel 存在哪些問題。

    阻塞模式

    我們先看服務(wù)端方法:

        public static void main(String[] args) throws Exception {
    ByteBuffer buffer = ByteBuffer.allocate(100);
    ServerSocketChannel serverSocket = ServerSocketChannel.open()
    serverSocket.bind(new InetSocketAddress(8080));
    List channels = new ArrayList<>();
    while(true) {
    SocketChannel sc = serverSocket.accept();
    channels.add(sc);

    for (SocketChannel asc :channels) {
    asc.read(buffer);
    buffer.flip();
    ByteBufferUtil.debugAll(buffer);
    buffer.clear();
    }
    }
    }
    • 首先新建一個(gè) ServerSocketChannel 類,同時(shí)綁定 8080 端口
    • 然后 while(true)循環(huán)調(diào)用 accept()來建立連接,同時(shí)將該 SocketChannel 加入到 List 集合中,該集合用來裝所有與服務(wù)端建立連接的 SocketChannel
    • 最后接受客戶端發(fā)送來的數(shù)據(jù),打印出來

    現(xiàn)在我們來運(yùn)行下(這里就不寫客戶端程序了,就用 MAC 的 iTerm 來模擬即可)。需要開 2 個(gè)客戶端。

    我們先打開 client-01,然后發(fā)送一條 “hi,i am client-01”

    服務(wù)器運(yùn)行結(jié)果:

    服務(wù)端準(zhǔn)確無誤打印出 client-01 發(fā)送過來的消息(hi,i am client-01)。這個(gè)時(shí)候你再發(fā)一條消息:“hi,i am client-11”,你會(huì)驚奇地發(fā)現(xiàn),服務(wù)端竟然不輸出客戶端發(fā)來的消息。這個(gè)時(shí)候你再啟動(dòng) client-02,神奇的事情發(fā)生了,服務(wù)端把 client-01 發(fā)送的消息(hi,i am client-11)給打印出來了:

    為什么會(huì)出現(xiàn)這種神奇的現(xiàn)象?主要原因就是該 ServerSocketChannel 是阻塞模式,相關(guān)方法都會(huì)導(dǎo)致線程的阻塞,當(dāng) client-01 建立連接,第一次發(fā)送消息時(shí),服務(wù)端正常打印消息(hi,i am client-01),這時(shí)服務(wù)端又運(yùn)行到 accept(),注意這個(gè)方法是阻塞方法,如果沒有客戶端來建立連接,它會(huì)一直阻塞在這里,哪怕 client-01 再次發(fā)送消息(hi,i am client-11),服務(wù)端也不會(huì)打印。這時(shí) client-02 與服務(wù)端建立連接,服務(wù)端就不會(huì)阻塞,打印 client-01 第二次發(fā)來的消息(hi,i am client-11)。

    所以,阻塞模式存在如下缺陷

    • 單線程情況下,阻塞方法都會(huì)導(dǎo)致線程暫停
      • ServerSocketChannel.accept() 會(huì)在沒有連接建立時(shí)讓線程暫停,即使有客戶端向服務(wù)端發(fā)送消息,服務(wù)單也接收不到直到有新客戶端連接服務(wù)端,不再阻塞在accept()方法上。
      • SocketChannel.read() 會(huì)在通道中沒有數(shù)據(jù)可讀時(shí)讓線程暫停,即使之后有新客戶端向服務(wù)端發(fā)起連接請(qǐng)求也接受不了,直到讀取完畢,不再阻塞在read()方法上

    所以在單線程情況,服務(wù)端幾乎不可能正常工作。那多線程呢?多線程情況下,如果連接數(shù)過多,必然會(huì)導(dǎo)致 OOM,然后線程的上下文切換也會(huì)導(dǎo)致性能低下。

    非阻塞模式

    上面的阻塞模式幾乎導(dǎo)致整個(gè)服務(wù)端是可能使用的,我們是可以使用非阻塞模式來避免的。如下

        public static void main(String[] args) throws Exception {
    ByteBuffer buffer = ByteBuffer.allocate(100);
    ServerSocketChannel serverSocket = ServerSocketChannel.open();

    serverSocket.bind(new InetSocketAddress(8080));
    List channels = new ArrayList<>();
    while(true) {
    // 非阻塞模式
    serverSocket.configureBlocking(false);

    SocketChannel sc = serverSocket.accept();
    if (sc != null){
    channels.add(sc);
    }

    for (SocketChannel asc :channels) {
    asc.configureBlocking(false);

    int size = asc.read(buffer);
    if (size > 0) {
    buffer.flip();
    ByteBufferUtil.debugAll(buffer);
    buffer.clear();
    }
    }
    }
    }
    • 通過 ServerSocketChannel.configureBlocking(false) 將 serverSocket 設(shè)置為非阻塞模式,這樣 serverSocket 在調(diào)用 accept()方法時(shí)就不會(huì)阻塞了,如果沒有連接,則會(huì)返回 null
    • 通過 SocketChannel..configureBlocking(false) 將 asc 設(shè)置為非阻塞模式,這 asc 在調(diào)用 read() 方法就不會(huì)阻塞了,如果沒有可讀數(shù)據(jù),它則會(huì)返回 -1。

    非阻塞模式雖然不會(huì)影響業(yè)務(wù)的使用,但由于在 while(true) 循環(huán)里面,CPU 會(huì)一直處理運(yùn)行狀態(tài),占用和浪費(fèi) CPU 資源。

    所以,采用這種 while(true) 循環(huán)的暴力方式根本就不適合業(yè)務(wù)使用,對(duì)于 SocketChannel 而言,我們希望他只擔(dān)任一個(gè)通道,傳傳數(shù)據(jù)的角色即可,不需再有額外的角色了,故而我們不能放任他們,需要對(duì)其進(jìn)行統(tǒng)一管理,既要有管理器,有連接來了,我就告訴你該建立連接了,有要讀的數(shù)據(jù),我就告訴你可以讀數(shù)據(jù)了,這樣 SocketChannel 是不是就很爽了。在 NIO 中,這個(gè)管理器稱之為 Selector。

    瀏覽 62
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評(píng)論
    圖片
    表情
    推薦
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    黄色大电影在这 | 啪啪啪导航 | 在线看黄色视频网站 | 好吊妞视频在线观看 | 亚洲天堂高清无码 |