1. 程式人生 > >Java網路程式設計-IO多路複用(單執行緒)

Java網路程式設計-IO多路複用(單執行緒)

1. 簡介

IO多路複用(multiplexing)屬於同步IO網路模型

是以Reactor模式實現

常見的IO多路複用應用有:select、poll、epoll

本篇文章採用JavaNIO框架來實現單執行緒的IO多路複用

2. Reactor模式的組成角色

1. Reactor:負責派發IO事件給對應的角色處理。為了監聽IO事件,select必須實現在Reactor中。

2. Acceptor:負責接受client的連線,然後給client繫結一個Handler並註冊IO事件到Reactor上監聽

3. Handler:負責處理與client互動的事件或行為
。通常因為Handler要處理與所對應client互動的多個事件或行為,為了簡化設計,會以狀態模式來實現Handler。


3. 程式碼實現

[TCPReactor.java]

  1. // Reactor執行緒
  2. package server;  
  3. import java.io.IOException;  
  4. import java.net.InetSocketAddress;  
  5. import java.nio.channels.SelectionKey;  
  6. import java.nio.channels.Selector;  
  7. import
     java.nio.channels.ServerSocketChannel;  
  8. import java.util.Iterator;  
  9. import java.util.Set;  
  10. publicclass TCPReactor implements Runnable {  
  11.     privatefinal ServerSocketChannel ssc;  
  12.     privatefinal Selector selector;  
  13.     public TCPReactor(int port) throws IOException {  
  14.         selector = Selector.open();  
  15.         ssc = ServerSocketChannel.open();  
  16.         InetSocketAddress addr = new InetSocketAddress(port);  
  17.         ssc.socket().bind(addr); // 在ServerSocketChannel繫結監聽埠
  18.         ssc.configureBlocking(false); // 設定ServerSocketChannel為非阻塞
  19.         SelectionKey sk = ssc.register(selector, SelectionKey.OP_ACCEPT); // ServerSocketChannel向selector註冊一個OP_ACCEPT事件,然後返回該通道的key
  20.         sk.attach(new Acceptor(selector, ssc)); // 給定key一個附加的Acceptor物件
  21.     }  
  22.     @Override
  23.     publicvoid run() {  
  24.         while (!Thread.interrupted()) { // 線上程被中斷前持續執行
  25.             System.out.println("Waiting for new event on port: " + ssc.socket().getLocalPort() + "...");  
  26.             try {  
  27.                 if (selector.select() == 0// 若沒有事件就緒則不往下執行
  28.                     continue;  
  29.             } catch (IOException e) {  
  30.                 // TODO Auto-generated catch block
  31.                 e.printStackTrace();  
  32.             }  
  33.             Set<SelectionKey> selectedKeys = selector.selectedKeys(); // 取得所有已就緒事件的key集合
  34.             Iterator<SelectionKey> it = selectedKeys.iterator();  
  35.             while (it.hasNext()) {  
  36.                 dispatch((SelectionKey) (it.next())); // 根據事件的key進行排程
  37.                 it.remove();  
  38.             }  
  39.         }  
  40.     }  
  41.     /* 
  42.      * name: dispatch(SelectionKey key) 
  43.      * description: 排程方法,根據事件繫結的物件開新執行緒 
  44.      */
  45.     privatevoid dispatch(SelectionKey key) {  
  46.         Runnable r = (Runnable) (key.attachment()); // 根據事件之key繫結的物件開新執行緒
  47.         if (r != null)  
  48.             r.run();  
  49.     }  
  50. }  

[Acceptor.java]

  1. // 接受連線請求執行緒
  2. package server;  
  3. import java.io.IOException;  
  4. import java.nio.channels.SelectionKey;  
  5. import java.nio.channels.Selector;  
  6. import java.nio.channels.ServerSocketChannel;  
  7. import java.nio.channels.SocketChannel;  
  8. publicclass Acceptor implements Runnable {  
  9.     privatefinal ServerSocketChannel ssc;  
  10.     privatefinal Selector selector;  
  11.     public Acceptor(Selector selector, ServerSocketChannel ssc) {  
  12.         this.ssc=ssc;  
  13.         this.selector=selector;  
  14.     }  
  15.     @Override
  16.     publicvoid run() {  
  17.         try {  
  18.             SocketChannel sc= ssc.accept(); // 接受client連線請求
  19.             System.out.println(sc.socket().getRemoteSocketAddress().toString() + " is connected.");  
  20.             if(sc!=null) {  
  21.                 sc.configureBlocking(false); // 設定為非阻塞
  22.                 SelectionKey sk = sc.register(selector, SelectionKey.OP_READ); // SocketChannel向selector註冊一個OP_READ事件,然後返回該通道的key
  23.                 selector.wakeup(); // 使一個阻塞住的selector操作立即返回
  24.                 sk.attach(new TCPHandler(sk, sc)); // 給定key一個附加的TCPHandler物件
  25.             }  
  26.         } catch (IOException e) {  
  27.             // TODO Auto-generated catch block
  28.             e.printStackTrace();  
  29.         }  
  30.     }  
  31. }  

我們先來簡單點的,Handler不以​​狀態模式實現,只以比較直覺的方式實現。

[TCPHandler.java]

  1. // Handler執行緒
  2. package server;  
  3. import java.io.IOException;  
  4. import java.nio.ByteBuffer;  
  5. import java.nio.channels.SelectionKey;  
  6. import java.nio.channels.SocketChannel;  
  7. import java.util.concurrent.LinkedBlockingQueue;  
  8. import java.util.concurrent.ThreadPoolExecutor;  
  9. import java.util.concurrent.TimeUnit;  
  10. publicclass TCPHandler implements Runnable {  
  11.     privatefinal SelectionKey sk;  
  12.     private

    相關推薦

    Java網路程式設計-IO(執行)

    1. 簡介 IO多路複用(multiplexing)屬於同步IO網路模型 是以Reactor模式實現 常見的IO多路複用應用有:select、poll、epoll 本篇文章採用Java的NIO框架來實現單執行緒的IO多路複用

    網路程式設計基礎(5) : IO(Reactor)(主從式Reactor)

    1. 介紹 把Reactor拆成兩個角色Main Reactor及Sub Reactor,以提升效能與資源利用率​​。 Main Reactor:負責監聽外部的連線請求,並派發給Acceptor處理。故Main Reactor中的selector只有註冊OP_ACCEP

    Linux IO之epoll網路程式設計,高併發的使用例子 (含原始碼)

    #include <unistd.h> #include <sys/types.h> /* basic system data types */ #include <sys/socket.h> /* basic socket definiti

    網路通訊 :IO之select、poll、epoll詳解

     目前支援I/O多路複用的系統呼叫有 select,pselect,poll,epoll,I/O多路複用就是通過一種機制,一個程序可以監視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程式進行相應的讀寫操作。但select,pselect,poll,epoll

    【Socket程式設計】篇六之IO——select、poll、epoll

    在上一篇中,我簡單學習了 IO多路複用的基本概念,這裡我將初學其三種實現手段:select,poll,epoll。 I/O 多路複用是為了解決程序或執行緒阻塞到某個 I/O 系統呼叫而出現的技術,使程序或執行緒不阻塞於某個特定的 I/O 系統呼叫。 select()

    python 學習第二十五天(事件驅動和io

    事件驅動模型 原文連結:http://www.cnblogs.com/yuanchenqi/articles/5722574.html 上節的問題: 協程:遇到IO操作就切換。 但什麼時候切回去呢?怎麼確定IO操作完了?

    通過IO自定義非同步IO模組

    import select,socket class HttpRequest:#HTTP請求類 def __init__(self, sk, host, callback):#初始化 self.host = host self.socket = sk

    Linux下套接字詳解---epoll模式下的IO伺服器

    1 epoll模型簡介 epoll可是當前在Linux下開發大規模併發網路程式的熱門人選,epoll 在Linux2.6核心中正式引入,和select相似,其實都I/O多路複用技術而已,並沒有什麼神祕的。 其實在Linux下設計併發網路程式,向來不缺少方法,比如典型的Apache模型(Proce

    Linux IO之select

    Linux IO多路複用之select 首先,我我們來介紹一下什麼是IO多路複用: IO多路複用是指核心一旦發現程序指定的一個或者多個IO條件準備讀取,它就通知該程序。 IO多路複用適用如下場合: 當客戶處理多個描述符時(一般是互動式輸入和網路套介面),

    Linux IO之poll

    Linux IO多路複用之poll 前面介紹了select,今天來介紹poll,poll 是一種高階的輪詢的方法,通常用於伺服器端處理多個客戶端的請求的時候。 其作用於 select 很相似,但是較比 select 方法而言,效率更高,並且處理的連線個數不受核心的限制。 若是使用 s

    Day038--Python--Gevent , IO

    1. 協程:    gevent  (遇到IO自動切換) import gevent import time from gevent import monkey; monkey.patch_all() # ;相當於換行 def eat(name): print('%s

    【面試必問】支撐百萬併發的"IO"技術你瞭解嗎?

    多路複用其實並不是什麼新技術,它的作用是在一個通訊連線的基礎上可以同時進行多個請求響應處理。對於網路通訊來其實不存在這一說法,因為網路層面只負責資料傳輸;由於上層應用協議的制訂問題,導致了很多傳統服務並不能支援多路複用;如:http1.1,sqlserver和redis等等,雖然有些服務提供批量處理,但這

    IO 之epoll(高效併發伺服器)

      epoll 是在 2.6 核心中提出的,是之前的select和 poll的增強版本。相對於 select和 poll來說,epoll更加靈活,沒有描述符限制。epoll使用一個檔案描述符管理多個描述符,將使用者關係的檔案描述符的事件存放到核心的一個事件表中,這樣在使用者空間和核心空間

    IO 之poll(高效併發伺服器)

      poll() 的機制與 select() 類似,與 select() 在本質上沒有多大差別,管理多個描述符也是進行輪詢,根據描述符的狀態進行處理,但是poll() 沒有最大檔案描述符數量的限制(但是數量過大後效能也是會下降)。poll() 和 select() 同樣存在一個缺點就是,

    IO 之select(高效併發伺服器)

    一、I/O 多路複用概述   I/O 多路複用技術是為了解決程序或執行緒阻塞到某個 I/O 系統呼叫而出現的技術,使程序不阻塞於某個特定的 I/O 系統呼叫。   select,poll,epoll都是I/O多路複用的機制。I/O多路複用通過一種機制,可以監視多個描述符,一旦某個

    程序,執行,協程,io 總結

    併發:要做到同時服務多個客戶端,有三種技術 1.  程序並行,只能開到當前cpu個數的程序,但能用來處理計算型任務 ,開銷最大 2.  如果並行不必要,那麼可以考慮用執行緒併發,單位開銷比程序小很多     執行緒:併發(輪詢排程,遇到阻塞就切換)     只要是網路,就會有延遲,有延遲就阻塞,所以比

    flask 原始碼淺析(flask 如何處理請求(執行程序,IO))

    之前有閱讀過tornado 底層的實現,tornado 為了解決C10K 問題(沒聽說過C10K問題的請檢視: http://www.360doc.com/content/13/0522/18/1542811_287328391.shtml),在Linux 平臺下是使用了epoll(pyth

    事件驅動----IO

    1、事件驅動 一種程式設計正規化,程式的執行流由外部事件來決定。它的一個特點是包含一個事件迴圈,當外部事件發生時使用回撥機制來觸發相應的處理。 2、IO多路複用 (1)使用者空間與核心空間: 供作業系統使用的是核心空間,供使用者使用的是使用者空間。 假設一個作業系統4G記憶體,使

    python-day35(協程,IO)

    一. 協程   協程: 是單執行緒下的併發,又稱微執行緒,纖程,英文名Coroutine.    併發: 切換+儲存狀態     協程是一種使用者態的輕量級執行緒,即協程是由使用者程式自己控制排程的   協程特點:     1. 必須在只有一個單執行緒裡實現併發     2. 修改共享資

    day035協程、IO

      本節內容: 1、協程(重點:gevent) 2、IO多路複用 一、協程 1、引子 本節的主題是基於單執行緒來實現併發,即只用一個主執行緒(很明顯可利用的cpu只有一個)情況下實現併發, 為此我們需要先回顧下併發的本質:切換+儲存狀態   cpu正在執行一個任務,會在兩種