<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【死磕 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="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  特级毛片av | 五月婷婷六月色 | 中国婬乱a一级毛片多女 | 日韩免费精品一区二区三区色欲AV | 国产老熟女高潮毛片A片仙踪林 |