1 / 40

課程參與度之評估方式

課程參與度之評估方式. 上課時必須專心聽講,跟上進度,參與討論 扣分項目 玩線上遊戲一次扣 1 分 玩手機一次扣 1 分 睡覺一次扣 1 分 聊天一次扣 1 分 無法回答老師提出的問題一次扣 1 分 加分項目 主動回答老師的問題一次加 2 分 找出老師程式中的錯誤一次加 1 分 修正老師程式中的錯誤一次加 4 分. 網路程式設計 第九章 Java NIO. 鄧姚文. 大綱. NIO 常用類別 緩衝區 Buffer NIO 客戶端程式 NIO 伺服器. 前言. Java NIO

shilah
Télécharger la présentation

課程參與度之評估方式

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 課程參與度之評估方式 • 上課時必須專心聽講,跟上進度,參與討論 • 扣分項目 • 玩線上遊戲一次扣1分 • 玩手機一次扣1分 • 睡覺一次扣1分 • 聊天一次扣1分 • 無法回答老師提出的問題一次扣1分 • 加分項目 • 主動回答老師的問題一次加2分 • 找出老師程式中的錯誤一次加1分 • 修正老師程式中的錯誤一次加4分

  2. 網路程式設計第九章 JavaNIO 鄧姚文

  3. 大綱 NIO常用類別 緩衝區Buffer NIO客戶端程式 NIO伺服器

  4. 前言 • JavaNIO • Java語言原本的輸出入(Input/Output)是java.io套件 • JDK自從1.4版開始有了新的處理方式-NIO(New I/O) • NIO以「非等待式」或稱「非堵塞式」(non-blocking)的方式進行資料的接收 • 一種「觀察並通知」的機制 • 當需要讀取資料時,不用讓程式痴痴地等待資料的到來到達或到來 • NIO並不是要「取代」原java.io的功能,而是補充

  5. 前言 • NIO的套件 • java.nio.channels.* • 定義了各類通道(Channels) • java.nio.channels.spi.* • java.nio.channels套件中類別設計的「服務提供者」類別。 • java.nio.charset.* • 定義在轉換Unicode字元與位元組資料之間的傳換類別,或稱為解碼器與編碼器類別。 • java.nio.charset.spi.* • 類別CharsetProvider是java.nio.charset套件類的服務提供者類別。

  6. 9-1NIO常用類別 • InetSocketAddress • 代表了一個主機的資訊 • Channel • NIO使用Channel來產生與遠端主機或檔案的通道 • SocketChannel • 適用於TCP協定的資料讀取與傳送。 • SocketServerChannel • 用於TCP協定的伺服器,傾聽本機的埠號,等待外來的連線。 • DatagramChannel • 用於UDP協定的封包傳送。

  7. 9-1NIO常用類別 InetSocketAddressaddr = new InetSocketAddress("ptt.cc", 23); • InetSocketAddress • 代表了一個主機的資訊 • 常用的建構子有: • public InetSocketAddress(InetAddress addr, int port):由傳入值addr位址物件再加上port埠號值而生成物件。 • public InetSocketAddress(String host, int port):傳入主機名稱或IP位址的字串與port埠號,例如產生ptt.cc與port 23:

  8. 9-1NIO常用類別 InetSocketAddressaddr = new InetSocketAddress("ptt.cc", 23);SocketChannelchann = SocketChannel.open(addr); • Channel • SocketChannel:適用於TCP協定的資料讀取與傳送。 • SocketServerChannel:用於TCP協定的伺服器,傾聽本機的埠號,等待外來的連線。 • DatagramChannel:用於UDP協定的封包傳送。 • 通常使用TCP協定的SocketChannel類別,並呼叫SocketChannel.open(主機位址)方法得到與主機的連線通道物件: • open方法將建立連線通道,若失敗則會拋出輸出入例外,因此需預先處理IOException。

  9. 9-1NIO常用類別 • Channel • NIO對資料的讀取與傳送都需要使用到另一個要角「緩衝區Buffer」,讀取資料時使用read方法,由通道的另一端將資料讀取至緩衝區內。傳送資料時先將資料放在緩衝區中,再呼叫Channel的write方法,以送出資料: • SocketChannel類的的read與write方法規格如下: • public int read(ByteBuffer buf):讀取遠端資料並放置於緩衝區(buf)中。 • public int write(ByteBuffer):先將欲傳送出的資料放在緩衝區中,再傳送至遠端主機。

  10. 9-2 緩衝區Buffer • 緩衝區類別 • 常用的緩衝區類別有七種

  11. 9-2 緩衝區Buffer • ByteBuffer buf = ByteBuffer.allocateDirect(20); • ByteBufferbuf = ByteBuffer.allocate(20); • 緩衝區的分類 • 直接緩衝區 • 可跳過JVM的處理,直接對應到作業系統的記憶體來存取緩衝區資料 • 適合較大檔案 • 間接緩衝區 • 由JVM負責所有的容量分配與存取動作 • 使用上會耗費JVM本身所分配的記憶體 • 實務設計常採用間接緩衝區來處理

  12. 9-2 緩衝區Buffer ByteBufferbuf = ByteBuffer.allocate(10); byte[] bb = "abcxyz".getBytes(); buf.put(bb); • 將資料放入緩衝區 • 使用put方法將資料放入緩衝區 • ByteBuffer put(byte[] src) • 將一個byte陣列的資料放入緩衝區 • buf的內容將成為:

  13. ByteBuffer put(byte[] src, int offset, int length) • 將byte陣列中特定範圍的元素放入緩衝區,從陣列的offset位置開始取得length個元素 • ByteBuffer put(ByteBuffer src) • 將另一個ByteBuffer的內容放到目前的ByteBuffer中

  14. 9-2 緩衝區Buffer ByteBufferbuf = ByteBuffer.allocate(10); byte[] bb = "abcxyz".getBytes(); buf.put(bb, 4, 2); • 將資料放入緩衝區 • ByteBuffer put(byte[] src, int offset, int length) • 將byte陣列中特定範圍的元素放入緩衝區,從陣列的offset位置開始取得length個元素 • buf的內容將成為: • ByteBuffer put(ByteBuffer src) • 將另一個ByteBuffer的內容放到目前的ByteBuffer中

  15. 9-2 緩衝區Buffer ByteBufferbuf = ByteBuffer.allocate(10); byte[] bb = "abcxyz".getBytes(); buf.put(bb); ByteBuffer buf2 = ByteBuffer.allocate(10); buf2.put(buf); • 將資料放入緩衝區 • ByteBuffer put(ByteBuffer src) • 將另一個ByteBuffer的內容放到目前的ByteBuffer中 • buf2的內容將成為:

  16. 9-2 緩衝區Buffer ByteBufferbuf = ByteBuffer.allocate(10); System.out.println("容量:"+buf.capacity()); ▌程式片段執行結果 容量:10 • 操作緩衝區 • 一個緩衝區有四項重要資訊 • 容量(capacity) • 緩衝區可容納的最大資料個數

  17. 9-2 緩衝區Buffer buf.put("abcxyz".getBytes()); System.out.println("位置:"+buf.position()); ▌程式片段執行結果 位置:6 • 操作緩衝區 • 位置(position) • 下一個資料被讀取或被寫入的位置 • 緩衝區未放置任何資料時: • 放入6個字元時: • 如右圖

  18. 9-2 緩衝區Buffer buf.put("abcxyz".getBytes()); System.out.println("位置:"+buf.position()); System.out.println("限制:"+buf.limit()); ▌程式片段執行結果 位置:6限制:10 buf.put("abcxyz".getBytes()); buf.mark(); • 操作緩衝區 • 限制(limit) • 緩衝區中可用資料的最大索引值 • 標記(mark) • 提供設計者依需求自行設定一個標記位置,供後續處理 • 放入6個字元,並標記:

  19. 9-2 緩衝區Buffer buf.put("GH".getBytes()); • 操作緩衝區 • 標記(mark) • 緩衝區標記(M)如下圖所示: • 接著再放入2個字元: • 緩衝區的位置(P)與標記(M)如下圖所示:

  20. 9-2 緩衝區Buffer buf.reset(); • 操作緩衝區 • 標記(mark) • 呼叫reset(),使緩衝區的位置值回到先前標記的位置: • 緩衝區的位置(P)與標記(M)如下圖:

  21. 9-2 緩衝區Buffer • ByteBufferbuf = ByteBuffer.allocate(10); • buf.put(“abcxyz”.getBytes()); • buf.flip(); • System.out.println(“容量:”+buf.capacity()); • System.out.println(“限制:”+buf.limit()); • System.out.println("位置:"+buf.position()); ▌執行結果 容量:10 限制:6 位置:0 • 從緩衝區中取出資料 • 使用flip()方法,將限制值(limit,有效資料的位置)修改為目前的位置值,再將位置值設定回0,可從頭讀取緩衝區內的資料

  22. 9-2 緩衝區Buffer byte[] bb = new byte[buf.limit()]; buf.get(bb); System.out.println(new String(bb)); while(buf.hasRemaining()){ System.out.print((char)buf.get()); } • 從緩衝區中取出資料 • 使用flip()方法後,緩衝區將如下圖: • 再使用get方法將資料讀出: • 或是使用while迴圈並配合緩衝區的hasRemaining()方法將資料循序讀出:

  23. 9-2 緩衝區Buffer • 緩衝區的方法 • public final Buffer clear() • clear方法可將位置值設定為0,且將限制值設定為最大可容納的長度,使用clear方法通常是為了讓緩衝區可以重新放新的資料。 • public final Buffer rewind() • 可讓緩衝區再被讀取一次。 • public final boolean hasRemaining() • 緩衝區內是否還有未讀取的資料,回傳布林值。 • public final int remaining() • 回傳目前位置值與限制值之間的資料個數。

  24. 9-3NIO客戶端程式 01 package com.ch09; 02 import java.io.IOException; 03 import java.net.InetSocketAddress; 04 import java.nio.ByteBuffer; 05 import java.nio.channels.SocketChannel; 06 • 以NIO方式連線至遠端主機的埠號並讀取資料,需經過四個步驟: • Step1 建立與遠端主機的通道。 • Step2 準備緩衝區。 • Step3 讀取遠端主機的資料至緩衝區。 • Step4 列印緩衝區內的資料。 • 本例將連線至一FTP站(j.snpy.org,埠號21):

  25. 9-3NIO客戶端程式 07 public class NioFtpClient { 08 public static void main(String[] args) throws IOException { 09 InetSocketAddressaddr = 10 new InetSocketAddress("j.snpy.org", 21); 11 SocketChannelchann = SocketChannel.open(addr); 12 //準備緩衝區 13 ByteBufferbuf = ByteBuffer.allocate(1024); 14 //讀取資料 15 chann.read(buf); 16 System.out.println("緩衝區有效資料個數:"+buf.position()); 17 //將緩衝區的位置設定為0,準備一一讀出並列印 18 buf.flip(); 19 while(buf.hasRemaining()){ 20 System.out.print((char)buf.get()); 21 } 22 } 23 } 本例將連線至一FTP站(j.snpy.org,埠號21):

  26. 9-3NIO客戶端程式 ▌程式碼說明 ‧第11-13行,先產生InetSocketAddress位址物件,再以位址物件產生連線至該主機的SocketChannel。 ‧第13行,準備緩衝區物件buf,共1024個位置大小的空間。 ‧第17行,開始讀取由遠端主機傳來的資料,並放在buf中。 ‧第20行,呼叫flip()方法,先將緩衝區的位置值設為0,限制值設為有效資料的後一位置值。 ‧第21至23行,利用迴圈將所有有效資料一一印出。 ▌以下為執行結果,最後一行是FTP伺服器所傳來的資料 緩衝區內的有效資料個數:20 220 (vsFTPd 2.0.7) 本例將連線至一FTP站(j.snpy.org,埠號21):

  27. 9-3NIO客戶端程式 • 傳送資料 • 在讀取了伺服器傳送的資料後,可進行FTP命令的傳送,將“USER anonymous\n”字串資料傳送至FTP伺服器進行帳號驗證:

  28. 22 //命令傳送,帳號認證 23 buf.clear(); 24 buf.put("USER anonymous\n".getBytes()); 25 buf.flip(); 26 chann.write(buf); 27 buf.clear(); 28 //再次讀取資料 29 chann.read(buf); 30 //將緩衝區的位置設定為0,準備一一讀出並列印 31 buf.flip(); 32 while(buf.hasRemaining()){ 33 System.out.print((char)buf.get()); 34 }

  29. 9-3NIO客戶端程式 • 緩衝區內的有效資料個數:20 • 220 (vsFTPd 2.0.7) • 331 Please specify the password. ▌程式碼說明 ‧第23行,將緩衝區的位置值歸零,以便後續可再放新的資料。 ‧第24行,將認證字串放入緩衝區。 ‧第25行,將緩衝區的位置值設回資料前端,以便後續將緩衝區資料傳送出去。 ‧第26行,傳送緩衝區資料至FTP伺服器端。 ‧第27行,清除緩衝區。 ‧第32行至34行,讀取FTP伺服器傳來的下一個回應訊息。 • 傳送資料 • 因為傳送了認證命令,伺服器會再傳送回應訊息,要求再傳送密碼:

  30. 9-4NIO伺服器 伺服器程式時更能夠顯出其優勢 使用NIO設計伺服器程式可以讓主程式(main執行緒)處理多個客戶端的需求,而不需要為每個客戶端產生個別的執行緒 一個客戶端的服務包含兩個通道(Channels),一是等待客戶端連線的「連線通道」ServerSocketChannel,另一個則是專門處理資料接收與傳送的「傳輸通道」SocketChannel

  31. 9-4NIO伺服器 • 「選擇器」Selector • 選擇器可容納需要觀察的通道,如連線通道與傳輸通道 • 各個通道使用register()方法把自己「註冊」(或登記)在選擇器中 • 有任何已註冊的動作(SelectionKey)發生時,可由選擇器的selectedKey()方法得到一個已發生的動作集合

  32. 9-4NIO伺服器 • 處理模式 • 使用ServerSocketChannel,一開始是傾聽並接受連線 • 待接收客戶端的連線之後,再轉換為資料傳輸的SocketChannel

  33. 9-4NIO伺服器 • 處理步驟 • 建立ServerSocketChannel • 註冊動作ACCEPT至選擇器selector • 準備監視迴圈 • 檢查所有發生的動作鍵值 • 產生該連線的SocketChannel • 利用資料傳輸通道送出資料

  34. 9-4NIO伺服器 01 package com.ch09; 02 03 import java.io.IOException; 04 import java.net.InetSocketAddress; 05 import java.net.ServerSocket; 06 import java.nio.ByteBuffer; 07 import java.nio.channels.SelectionKey; 08 import java.nio.channels.Selector; 09 import java.nio.channels.ServerSocketChannel; 10 import java.nio.channels.SocketChannel; 11 import java.util.Iterator; 12 import java.util.Set; 13 14 public class NioSimpleServer { 15 public static void main(String[] args) throws IOException { 16 ServerSocketChannelserverChannel = 17 ServerSocketChannel.open(); 18 ServerSocketss = serverChannel.socket();

  35. 9-4NIO伺服器 19 ss.bind(new InetSocketAddress(9950)); 20 serverChannel.configureBlocking(false); 21 Selector selector = Selector.open(); 22 serverChannel.register(selector, SelectionKey.OP_ACCEPT); 23 while (true) { 24 selector.select(); 25 Set keys = selector.selectedKeys(); 26 Iterator it = keys.iterator(); 27 while (it.hasNext()) { 28 SelectionKey key = (SelectionKey) it.next(); 29 it.remove(); 30 if (key.isAcceptable()) { 31 System.out.println("client connected"); 32 ServerSocketChannel server = 33 (ServerSocketChannel) key.channel(); 34 SocketChannel client = server.accept(); 35 client.configureBlocking(false); 36 SelectionKeyclientKey =

  36. 9-4NIO伺服器 37 client.register(selector, SelectionKey.OP_WRITE); 38 } else if (key.isWritable()) { 39 SocketChannel client = (SocketChannel) key.channel(); 40 ByteBuffer buff = ByteBuffer.allocate(10); 41 buff.put("ABC".getBytes()); 42 buff.flip(); 43 client.write(buff); 44 client.close(); 45 } 46 } 47 } 48 } 49 } ▌程式碼說明 ‧第16-17行,利用ServerSocketChannel的靜態方法open()產生serverChannel物件。

  37. 9-4NIO伺服器 ‧第18行,得到ServerSocket物件ss。 ‧第19行,將伺服器通道傾聽的埠號綁定為9950。 ‧第20行,通道設定為Non-blocking模式。 ‧第21行,呼叫Selector類別的靜態方法open()產生selector物件。 ‧第22行,將伺服器通道的接收連線事件註冊至selector選擇器,以便日後用戶端連線時可以得到通知。 ‧第23-47行,持續監視(傾聽)迴圈。 ‧第24行,呼叫選擇器的select()方法,進行等待註冊事件的發生,當有事件發生時,會進行下一行的處理。 ‧第25行,事件發生(可能是有客戶端連線),利用選擇器的selectedKeys()方法取得所有事件的鍵值,並放入Set集合物件keys。 ‧第26行,取得集合內的所有元素it。 ‧第27-46行,利用while迴圈走訪Iterator集合內的每一個元素。 ‧第28-29行,得到元素內的SelectionKey物件key,再自集合中移除該元素。 ‧第30-37行,若key值是剛連上線的事件,則印出訊息並取得該連線的通道物件client,再註冊其資料寫入的事件至選擇器中。 ‧第38-45行,若是資料寫入事件發生,即取得通道物件client,並以緩衝區方式,送出"ABC"字串至用戶端,最後關閉連線。

  38. 9-4NIO伺服器 telnet localhost 9950 • 完整程式碼與解說 • 執行與測試 • 執行本類別後,會持續傾聽9950埠號,接著使用Windows系統內附的telnet程式即可測試本伺服器運作,讀者可開啟「/開始/程式集/附屬應用程式/命令提示字元」,再輸入: • 即可收到伺服器回應的"ABC"字串,如下圖:

  39. 本章結束 Q&A討論時間

More Related