Java NIO 非阻塞Socket伺服器構建

來源:互聯網
上載者:User

標籤:

部分內容引用自xpbug的Blog。

說到socket伺服器,第一反應是java.net.Socket這個類。事實上在並發和回應時間要求不高的場合,是可以用java.net.Socket來實現的,比如寫一個區域網路聊天工具、傳送檔案等。但它的缺點也很明顯,需要自行對接受的線程進行維護,管理緩衝區的分配等,我嘗試過用java.net.Socket完成一個瞬時負載在千人左右的伺服器,卻因後期改動和維護異常麻煩而放棄。

Java自1.4以後,加入了新IO特性,這便是本文要介紹的NIO。下面是一段伺服器的範例程式碼:

  1 public class EchoServer {  2     public static SelectorLoop connectionBell;  3     public static SelectorLoop readBell;  4     public boolean isReadBellRunning=false;  5    6     public static void main(String[] args) throws IOException {  7         new EchoServer().startServer();  8     }  9       10     // 啟動伺服器 11     public void startServer() throws IOException { 12         // 準備好一個鬧鐘.當有連結進來的時候響. 13         connectionBell = new SelectorLoop(); 14           15         // 準備好一個鬧裝,當有read事件進來的時候響. 16         readBell = new SelectorLoop(); 17           18         // 開啟一個server channel來監聽 19         ServerSocketChannel ssc = ServerSocketChannel.open(); 20         // 開啟非阻塞模式 21         ssc.configureBlocking(false); 22           23         ServerSocket socket = ssc.socket(); 24         socket.bind(new InetSocketAddress("localhost",7878)); 25           26         // 給鬧鐘規定好要監聽報告的事件,這個鬧鐘只監聽新串連事件. 27         ssc.register(connectionBell.getSelector(), SelectionKey.OP_ACCEPT); 28         new Thread(connectionBell).start(); 29     } 30       31     // Selector輪詢線程類 32     public class SelectorLoop implements Runnable { 33         private Selector selector; 34         private ByteBuffer temp = ByteBuffer.allocate(1024); 35           36         public SelectorLoop() throws IOException { 37             this.selector = Selector.open(); 38         } 39           40         public Selector getSelector() { 41             return this.selector; 42         } 43   44         @Override 45         public void run() { 46             while(true) { 47                 try { 48                     // 阻塞,只有當至少一個註冊的事件發生的時候才會繼續. 49                     this.selector.select(); 50                       51                     Set<SelectionKey> selectKeys = this.selector.selectedKeys(); 52                     Iterator<SelectionKey> it = selectKeys.iterator(); 53                     while (it.hasNext()) { 54                         SelectionKey key = it.next(); 55                         it.remove(); 56                         // 處理事件. 可以用多線程來處理. 57                         this.dispatch(key); 58                     } 59                 } catch (IOException e) { 60                     e.printStackTrace(); 61                 } catch (InterruptedException e) { 62                     e.printStackTrace(); 63                 } 64             } 65         } 66           67         public void dispatch(SelectionKey key) throws IOException, InterruptedException { 68             if (key.isAcceptable()) { 69                 // 這是一個connection accept事件, 並且這個事件是註冊在serversocketchannel上的. 70                 ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); 71                 // 接受一個串連. 72                 SocketChannel sc = ssc.accept(); 73                   74                 // 對新的串連的channel註冊read事件. 使用readBell鬧鐘. 75                 sc.configureBlocking(false); 76                 sc.register(readBell.getSelector(), SelectionKey.OP_READ); 77                   78                 // 如果讀取線程還沒有啟動,那就啟動一個讀取線程. 79                 synchronized(EchoServer.this) { 80                     if (!EchoServer.this.isReadBellRunning) { 81                         EchoServer.this.isReadBellRunning = true; 82                         new Thread(readBell).start(); 83                     } 84                 } 85                   86             } else if (key.isReadable()) { 87                 // 這是一個read事件,並且這個事件是註冊在socketchannel上的. 88                 SocketChannel sc = (SocketChannel) key.channel(); 89                 // 寫資料到buffer 90                 int count = sc.read(temp); 91                 if (count < 0) { 92                     // 用戶端已經中斷連線. 93                     key.cancel(); 94                     sc.close(); 95                     return; 96                 } 97                 // 切換buffer到讀狀態,內部指標歸位. 98                 temp.flip(); 99                 String msg = Charset.forName("UTF-8").decode(temp).toString();100                 System.out.println("Server received ["+msg+"] from client address:" + sc.getRemoteAddress());101                  102                 Thread.sleep(1000);103                 // echo back.104                 sc.write(ByteBuffer.wrap(msg.getBytes(Charset.forName("UTF-8"))));105                  106                 // 清空buffer107                 temp.clear();108             }109         }111     }112  113 }

 

 OK,原文的注釋已經很詳細了,這裡進一步解析這段代碼。

首先是java.nio.channels.ServerSocketChannel這個類

Java NIO 非阻塞Socket伺服器構建

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.