Java之NIO

想要學習Java的Socket通訊,首先要學習Java的IO和NIO基礎,這方面能夠閱讀《Java NIO 系列教程》。  html

下面展現本身代碼熟悉Java的NIO編程的筆記。java

 

一、緩衝區(Buffer)web

/*
 * 1、緩衝區(Buffer):在Java 中負責數據的存取。緩衝區就是數組。用於存儲不一樣數據類型的數據
 *       
 *       根據數據類型不一樣(boolean除外),提供了相應類型的緩衝區
 *         ByteBuffer 那麼實際上最經常使用的,由於網絡傳輸的字節就是Byte
 *       CharBuffer
 *         ShortBuffer
 *         IntBuffer
 *         LongBuffer
 *       FloatBuffer
 *       DoubleBuffer
 *     
 *    上述緩衝區的管理方式幾乎一致,經過allocate()獲取緩衝區
 *    
 *  2、緩衝區存儲數據的兩個核心方法
 *      put()  : 存入數據到緩衝區中
 *      get()  : 獲取緩衝區中的數據
 *      
 *  3、緩衝區中的四個核心屬性
 *      capacity : 容量,表示緩衝區中最大存儲數據的容量
 *      limit    : 界限,標識緩衝區中能夠操做數據的大小(limit後面的數據不能進行讀寫的)
 *      position : 位置,標識緩衝區中正在操做數據的位置
 *      
 *      mark     : 標記,標識記錄當前position的位置,能夠經過reset()恢復到mark的位置
 *  
 *      0 <= mark <= position <= limit <= capacity
 *  4、直接緩衝區和非直接緩衝區
 *      非直接緩衝區: 經過allocate()方法分配緩衝區,將緩衝區創建在JVM的內存中。
 *      直接緩衝區 :  經過allockateDirect()方法分配直接緩存區,將緩衝區創建在物理內存中。能夠提升效率。
 *                  若是有數據須要一直在內存空間中重複用,而且數據量不大的狀況下,就用allockateDirect()這種方法。
 *                  畢竟內存空間是有限的。
 */編程

相關代碼:數組

  1 package com.demo.test;
  2 
  3 import java.nio.ByteBuffer;
  4 
  5 public class TestBuffer {
  6     
  7     public void test3(){
  8         // 分配直接緩衝區
  9         ByteBuffer buf = ByteBuffer.allocate(1024);
 10         System.out.println(buf.isDirect());// 判斷這個緩衝區是直接仍是非直接的。
 11     }
 12     
 13     public void test2(){
 14         String str = "abcde";
 15         
 16         // 一、分配一個指定大小的緩衝區
 17         ByteBuffer buf = ByteBuffer.allocate(1024);
 18         
 19         // 二、利用put()存入數據到緩衝區中
 20         System.out.println("往buf插入全部字符串的bytes是:"+str.getBytes());
 21         buf.put(str.getBytes());
 22         
 23         buf.flip();// 將插入模式轉爲查詢模式,就是查詢position位置會回到0
 24         
 25         System.out.println("建立和緩衝區等長度的字節數組,用來從緩衝區取出字節並存儲以待讀取操做");
 26         byte[] dst  = new byte[buf.limit()];
 27         System.out.println("從緩衝區中去除0開始的2位字節數據放進數組dst中");
 28         buf.get(dst, 0, 2);
 29         System.out.println("打印dst字節數組"+new String(dst, 0, 2));
 30         System.out.println("如今緩衝區的操做起始位置:"+buf.position());
 31         
 32         // mark(); 標記
 33         System.out.println("標記");
 34         buf.mark();
 35         buf.get(dst, 2, 2);// 取出從2開始的2位字節數據放進
 36         System.out.println("打印dst字節數組"+new String(dst, 2, 2));
 37         System.out.println("如今緩衝區的操做起始位置:"+buf.position());
 38         
 39         // reset(); 回覆到mark的位置
 40         buf.reset();
 41         System.out.println("reset重置以後,緩衝區的操做起始位置回到:"+buf.position());
 42         
 43         // 判斷緩衝區中是否還有剩餘的數據
 44         if(buf.hasRemaining()){
 45             // 獲取緩衝區還能夠操做的數量
 46             System.out.println("緩衝區還有能夠操做的數量"+buf.remaining());
 47         }
 48     }
 49     
 50     public void test(){
 51         String str = "abcde";
 52         
 53         // 一、分配一個指定大小的緩衝區
 54         ByteBuffer buf = ByteBuffer.allocate(1024);
 55         System.out.println("--調用allocate以後--");
 56         System.out.println(buf.capacity());
 57         System.out.println(buf.limit());
 58         System.out.println("position:"+buf.position());
 59         
 60         // 二、利用put()存入數據到緩衝區中
 61         System.out.println("字符串的bytes是:"+str.getBytes());
 62         buf.put(str.getBytes());
 63 
 64         System.out.println("--調用put以後--");
 65         System.out.println(buf.capacity());
 66         System.out.println(buf.limit());
 67         System.out.println("position:"+buf.position());
 68         
 69         // 三、前面是存入數據的模式,存入5個字節以後,position的位置從本來0如今到5了
 70         buf.flip();
 71         //   如今要將存儲數據模式改爲讀取模式,position的位置會變回爲0
 72         
 73         System.out.println("--調用flip切換模式以後--");
 74         System.out.println(buf.capacity());
 75         System.out.println(buf.limit());
 76         System.out.println("position:"+buf.position());
 77         
 78         // 四、利用get()讀取緩衝區中的數據
 79         byte[] dst = new byte[buf.limit()];// 建立和緩衝區同樣大的字節數據
 80         buf.get(dst);
 81         System.out.println(new String(dst,0,dst.length));
 82         
 83         System.out.println("--調用get()切換模式以後--");
 84         System.out.println(buf.capacity());
 85         System.out.println(buf.limit());
 86         System.out.println("position:"+buf.position());// 打印輸出的結果會發現position變回5了
 87         
 88         // 五、可重複讀取
 89         buf.rewind();
 90         
 91         System.out.println("--調用rewind()切換模式以後--");
 92         System.out.println(buf.capacity());
 93         System.out.println(buf.limit());
 94         System.out.println("position:"+buf.position());
 95         
 96         // 六、清空緩衝區。可是緩衝區中的數據依然存在,可是出於「被遺忘」狀態
 97         buf.clear();
 98         
 99         System.out.println("--調用clear()切換模式以後--");
100         System.out.println(buf.capacity());
101         System.out.println(buf.limit());
102         System.out.println("position:"+buf.position());
103         
104         // 看看緩衝區有沒有數據
105         System.out.println((char)(buf.get()+1));
106         
107     }
108 }

 二、通道(Channel)緩存

由java.nio.channels包定義的。Channel表示IO源與目標打開的鏈接。Channel相似於傳統的「流」。只不過Channel自己不能直接訪問數據,Channel只能與Buffer進行交互。服務器

先完成文件的複製:網絡

  1 /*
  2  * 1、通道(Channel):用於源節點與目標節點的鏈接。在Java NIO中負責緩衝區中數據的傳輸。
  3  *                      Channel自己不存儲數據,所以須要配合緩衝區進行傳輸
  4  * 
  5  * 2、通道的主要實現類
  6  *         java.nio.channels.Channel接口
  7  *             |--FileChannel
  8  *             |--SocketChannel
  9  *          |--ServerSocketChannel
 10  *            |--DatagramChannel
 11  *    
 12  * 3、獲取通道
 13  *         一、Java針對支持通道的類提供了getChannel()方法
 14  *             本地:
 15  *                 FileInputStream/FileOutputStream
 16  *                 RandomAccessFile隨機存儲文件流
 17  *             網絡:
 18  *                 Socket
 19  *                 ServerSocket
 20  *                 DatagramSocket
 21  * 
 22  *         二、在JDK1.7中的NIO.2針對各個通道提供了一個靜態的方法
 23  *         三、在JDK1.7中的NIO.2的Files工具類的newByteChannel()
 24  *             
 25  * 4、通道之間的數據傳輸
 26  *         transferFrom()
 27  *         transferTo()
 28  * 
 29  * 5、分散(Scatter)和彙集(Gather)
 30  *         分散讀取(Scattering Reads):將通道中的數據分散到多個緩衝區中
 31  *         彙集寫入(Gathering Writes):將多個緩衝區中的數據彙集到通道中
 32  */
 33 public class TestChannel {
 34     // 一、利用通道完成文件的複製(非直接緩衝區)
 35     public void    test(){
 36         long start = System.currentTimeMillis();
 37         
 38         FileInputStream fis = null;
 39         FileOutputStream fos = null;
 40         // 獲取通道
 41         FileChannel inChannel = null;
 42         FileChannel outChannel = null;
 43         try {
 44             fis = new FileInputStream("1.png");
 45             fos = new FileOutputStream("2.png");
 46             inChannel = fis.getChannel();
 47             outChannel = fos.getChannel();
 48             // 分批額指定大小的緩衝區
 49             ByteBuffer buf = ByteBuffer.allocate(1024);
 50             
 51             // 將通道中的數據存入緩衝區中
 52             while(inChannel.read(buf)!=-1){
 53                 buf.flip();//切換讀取數據的模式
 54                 // 將緩衝區中的數據寫入通道中
 55                 outChannel.write(buf);
 56                 buf.clear();
 57             }
 58         } catch (IOException e) {
 59             // TODO Auto-generated catch block
 60             e.printStackTrace();
 61         } finally {
 62             if(outChannel != null){
 63                 try {
 64                     outChannel.close();
 65                 } catch (IOException e) {
 66                     // TODO Auto-generated catch block
 67                     e.printStackTrace();
 68                 }
 69             }
 70             if(inChannel != null){
 71                 try {
 72                     inChannel.close();
 73                 } catch (IOException e) {
 74                     // TODO Auto-generated catch block
 75                     e.printStackTrace();
 76                 }
 77             }
 78             if(fos != null){
 79                 try {
 80                     fos.close();
 81                 } catch (IOException e) {
 82                     // TODO Auto-generated catch block
 83                     e.printStackTrace();
 84                 }
 85             }
 86             if(fis != null){
 87                 try {
 88                     fis.close();
 89                 } catch (IOException e) {
 90                     // TODO Auto-generated catch block
 91                     e.printStackTrace();
 92                 }
 93             }
 94         }
 95         long end = System.currentTimeMillis();
 96         System.out.println("一、耗費的時間:"+(end - start));
 97         
 98     }
 99     
100     // 二、利用通道完成文件的複製(直接緩衝區,內存映射文件的方式)
101     public void test2(){
102         
103         long start = System.currentTimeMillis();
104         
105         FileChannel inChannel = null;
106         // CREATE_NEW 存在就報錯
107         FileChannel outChannel = null;
108         try {
109             inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ); 
110             outChannel = FileChannel.open(Paths.get("3.png"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
111             
112             // 物理內存映射文件
113             MappedByteBuffer inMapByteBuffer = inChannel.map(MapMode.READ_ONLY,0,inChannel.size());
114             MappedByteBuffer outMapByteBuffer = outChannel.map(MapMode.READ_WRITE,0,inChannel.size());
115             
116             // 直接對緩衝區進行數據的讀寫操做
117             byte[] dst = new byte[inMapByteBuffer.limit()];
118             inMapByteBuffer.get(dst);
119             outMapByteBuffer.put(dst);
120         } catch (IOException e) {
121             // TODO Auto-generated catch block
122             e.printStackTrace();
123         } finally{
124             if(inChannel!=null){                
125                 try {
126                     inChannel.close();
127                 } catch (IOException e) {
128                     // TODO Auto-generated catch block
129                     e.printStackTrace();
130                 }
131             }
132             if(outChannel!=null){                
133                 try {
134                     outChannel.close();
135                 } catch (IOException e) {
136                     // TODO Auto-generated catch block
137                     e.printStackTrace();
138                 }
139             }
140         }
141         
142         long end = System.currentTimeMillis();
143         System.out.println("二、耗費的時間:"+(end - start));
144         
145     }
146     
147     // 通道之間的數據傳輸
148     public void test3(){
149         FileChannel inChannel = null;
150         // CREATE_NEW 存在就報錯
151         FileChannel outChannel = null;
152         
153         try {
154             inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ); 
155             outChannel = FileChannel.open(Paths.get("4.png"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
156             
157 //        inChannel.transferTo(0, inChannel.size(), outChannel);
158             outChannel.transferFrom(inChannel, 0, inChannel.size());
159         } catch (IOException e) {
160             // TODO Auto-generated catch block
161             e.printStackTrace();
162         } finally{
163             if(inChannel!=null){                
164                 try {
165                     inChannel.close();
166                 } catch (IOException e) {
167                     // TODO Auto-generated catch block
168                     e.printStackTrace();
169                 }
170             }
171             if(outChannel!=null){                
172                 try {
173                     outChannel.close();
174                 } catch (IOException e) {
175                     // TODO Auto-generated catch block
176                     e.printStackTrace();
177                 }
178             }
179         }
180         
181     }
182     
183     // 分散(Scatter)和彙集(Gather)
184     public void test4(){
185         // 文件路徑: webapp/index.html
186         try {
187             RandomAccessFile raFile = new RandomAccessFile("webapp/index.html", "rw");
188             FileChannel fileChannel = raFile.getChannel();
189             
190             ByteBuffer tBuffer = ByteBuffer.allocate(24);
191             ByteBuffer tBuffer2 = ByteBuffer.allocate(80);
192             
193             // 分散讀取到兩個字節緩衝區數組中
194             ByteBuffer[] bBuffers = {tBuffer,tBuffer2};
195             fileChannel.read(bBuffers);
196             // 打印以前須要修改一下緩衝區的寫入模式爲讀取模式
197             for (ByteBuffer byteBuffer : bBuffers) {
198                 byteBuffer.flip();
199             }
200             System.out.println(new String(bBuffers[0].array(),0,bBuffers[0].limit()));
201             System.out.println("===========");
202             System.out.println(new String(bBuffers[1].array(),0,bBuffers[1].limit()));
203             
204             
205             // 彙集寫入到通道中
206             RandomAccessFile randomAccFile2 = new RandomAccessFile("test.html", "rw");
207             FileChannel fileChannel2 = randomAccFile2.getChannel();
208             
209             fileChannel2.write(bBuffers);
210             
211             
212         } catch (IOException e) {
213             // TODO Auto-generated catch block
214             e.printStackTrace();
215         }
216     }
217 }

>>一、字節緩衝區要麼是直接的,要麼是非直接的。若是爲直接字節緩衝區,則Java虛擬機會盡最大努力直接在 此緩衝區上執行本機 I/O 操做。也就是說,在每次調用基礎操做系統的一個本機 I/O 操做以前(或以後), 虛擬機都會盡可能避免將緩衝區的內容複製到中間緩衝區中(或從中間緩衝區中複製內容)。併發


>>二、直接字節緩衝區能夠經過調用此類的allocateDirect()工廠方法來建立。此方法返回的緩衝區進行分配和取消 分配所需成本一般高於非直接緩衝區。直接緩衝區的內容能夠駐留在常規的垃圾回收堆以外,所以,它們對 應用程序的內存需求量形成的影響可能並不明顯。因此,建議將直接緩衝區主要分配給那些易受基礎系統的 本機 I/O 操做影響的大型、持久的緩衝區。通常狀況下,最好僅在直接緩衝區能在程序性能方面帶來明顯好 處時分配它們。app


>>三、直接字節緩衝區還能夠經過FileChannel的map()方法將文件區域直接映射到內存中來建立。該方法返回 MappedByteBuffer 。Java 平臺的實現有助於經過 JNI 從本機代碼建立直接字節緩衝區。若是以上這些緩衝區 中的某個緩衝區實例指的是不可訪問的內存區域,則試圖訪問該區域不會更改該緩衝區的內容,而且將會在 訪問期間或稍後的某個時間致使拋出不肯定的異常。


>>四、字節緩衝區是直接緩衝區仍是非直接緩衝區可經過調用其isDirect()方法來肯定。提供此方法是爲了可以在 性能關鍵型代碼中執行顯式緩衝區管理。

 

三、字符集(Charset)

  
    編碼:字符串 --> 字節數組 將字符序列轉爲字節序列的過程叫編碼
    解碼:字節數組 --> 字符串 將字節序列轉爲字符序列的過程叫解碼

代碼:

 1 // 編碼解碼
 2     public void test6(){
 3         // 獲取字符集合中的GBK字符集
 4         Charset cs1 = Charset.forName("GBK");
 5         
 6         // 獲取編碼器
 7         CharsetEncoder ce = cs1.newEncoder();
 8         // 獲取解碼器
 9         CharsetDecoder cd = cs1.newDecoder();
10         
11         // 建立字符串緩衝區
12         CharBuffer cBuf = CharBuffer.allocate(1024);
13         cBuf.put("何楊很帥!");
14         cBuf.flip();
15         
16         try {
17             // 開始編碼
18             ByteBuffer bBuf = ce.encode(cBuf);
19             System.out.println("用GBK編碼以後:");
20             for (int i = 0; i < bBuf.limit(); i++) {
21                 System.out.println(bBuf.get());
22             }
23             
24             // 而後開始解碼回去
25             bBuf.flip();
26             CharBuffer cBuf2 = cd.decode(bBuf);
27             System.out.println("用GBK解碼以後:");
28             System.out.println(cBuf2.toString());
29             
30             System.out.println("------------");
31             
32             System.out.println("獲取UTF-8字符集");
33             Charset cs2 = Charset.forName("UTF-8");
34             bBuf.flip();
35             CharBuffer cBuf3 = cs2.decode(bBuf);// 也能夠直接用字符集進行解碼
36             
37             System.out.println("用字符集進行解碼以後:"+cBuf3.toString());
38             
39             
40         } catch (CharacterCodingException e) {
41             // TODO Auto-generated catch block
42             e.printStackTrace();
43         }
44         
45     }

 四、使用NIO完成網絡通訊

 1 /*
 2  * 1、使用NIO完成網絡通訊的三個核心:
 3  *         一、通道(Channel):負責鏈接
 4  *             java.nio.channels.Channel 接口:
 5  *                 |--SelectableChannel
 6  *                 |--ServerSocketChannel
 7  *                 |--DatagramChannel
 8  * 
 9  *                 |--Pipe.SinkChannel
10  *                 |--Pipe.SourceChannel
11  * 
12  *         二、緩衝區(Buffer):負責數據的存取
13  *     
14  *         三、選擇器(Selector):是SelectableChannel的多路複合器。
15  *                             用於監控SelectableChannel的IO情況
16  */

4-一、下面先實現阻塞式通道

 1 public class TestBlockingNIO {
 2     // 客戶端
 3     @Test
 4     public void client(){
 5         try {
 6             System.out.println("客戶端開始建立。");
 7             // 一、獲取Socket通道
 8             SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
 9             // 二、分配指定大小的緩衝區
10             ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
11             // 三、讀取本地文件,併發到服務端
12             // 先須要文件通道獲取本地文件
13             FileChannel fileChannel = FileChannel.open(Paths.get("2.png"), StandardOpenOption.READ);
14             System.out.println("開始發送圖片數據");
15             while (fileChannel.read(byteBuffer) != -1) {
16                 byteBuffer.flip();
17                 socketChannel.write(byteBuffer);
18                 byteBuffer.clear();
19                 System.out.println("發送一次");
20             }
21             
22             System.out.println("--開始中止輸出--");
23             // 四、客戶端發送完數據以後要中止通道的輸出
24             socketChannel.shutdownOutput();
25             // --發送完了圖片數據以後纔會進行下面一步,其實這裏也是單線程阻塞的。--
26             System.out.println("--發送完了圖片數據以後纔會進行下面一步,其實這裏也是單線程阻塞的。--");
27             
28             // 五、接收服務端的反饋
29             int len = 0;
30             while((len = socketChannel.read(byteBuffer))!=-1){
31                 byteBuffer.flip();
32                 System.out.println(new String(byteBuffer.array(), 0, len));
33                 byteBuffer.clear();
34                 
35             }
36             socketChannel.shutdownInput();// 中止客戶端的接收輸入
37             
38             // 六、關閉通道
39             fileChannel.close();
40             socketChannel.close();
41             
42         } catch (IOException e) {
43             // TODO Auto-generated catch block
44             e.printStackTrace();
45         }
46     }
47     
48     // 服務端
49     @Test
50     public void server(){
51         try {
52             System.out.println("服務器開始啓動。。。");
53             // 一、獲取服務端SocketChannel
54             ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
55             // 二、綁定鏈接端口號
56             serverSocketChannel.bind(new InetSocketAddress(9898));
57             // 三、獲取客戶端鏈接的通道
58             SocketChannel sChannel = serverSocketChannel.accept();
59             
60             // 四、建立一個指定大小的緩衝區
61             ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
62             
63             // 五、接收的數據通道也要文件通道 StandardOpenOption.CREATE表示有文件就覆蓋,沒文件就建立
64             FileChannel fileChannel = FileChannel.open(Paths.get("4.png"), StandardOpenOption.WRITE,StandardOpenOption.CREATE);
65             
66             System.out.println("開始接收圖片數據");
67             while (sChannel.read(byteBuffer)!=-1) {
68                 byteBuffer.flip();
69                 fileChannel.write(byteBuffer);
70                 byteBuffer.clear();
71                 System.out.println("接收一次");
72             }
73             
74             System.out.println("接收完畢客戶端發來的數據,開始反饋數據給客戶端");
75             
76             // 六、發送反饋給客戶端
77             byteBuffer.put("服務器接收數據成功".getBytes());
78             byteBuffer.flip();
79             sChannel.write(byteBuffer);
80             
81             // 七、關閉通道
82             fileChannel.close();
83             sChannel.close();
84             serverSocketChannel.close();
85             
86         } catch (IOException e) {
87             // TODO Auto-generated catch block
88             e.printStackTrace();
89         }
90     }
91 }

 4-二、而後是實現非阻塞通道

  1 package com.demo.test;
  2 
  3 import java.io.IOException;
  4 import java.net.InetSocketAddress;
  5 import java.nio.ByteBuffer;
  6 import java.nio.channels.SelectionKey;
  7 import java.nio.channels.Selector;
  8 import java.nio.channels.ServerSocketChannel;
  9 import java.nio.channels.SocketChannel;
 10 import java.util.Iterator;
 11 import java.util.Scanner;
 12 
 13 import org.junit.Test;
 14 
 15 public class TestNonBlockingNIO {
 16     
 17     @Test
 18     public void startClient(){
 19         SocketChannel sChannel = null;
 20         // 獲取通道
 21         try {
 22             
 23             System.out.println("啓動客戶端");
 24             
 25             sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));
 26             
 27             // 切換非阻塞模式
 28             sChannel.configureBlocking(false);
 29             
 30             ByteBuffer buf = ByteBuffer.allocate(1024);
 31             
 32             System.out.println("開始經過輸入框輸入給服務器端發送數據");
 33             Scanner scan = new Scanner(System.in);
 34             while (scan.hasNext()) {
 35                 String str = scan.next();
 36                 buf.put(str.getBytes());
 37                 buf.flip();
 38                 sChannel.write(buf);
 39                 buf.clear();                
 40             }
 41             
 42             scan.close();
 43             
 44             
 45         } catch (IOException e) {
 46             // TODO Auto-generated catch block
 47             e.printStackTrace();
 48         } finally {
 49             if (sChannel != null) {
 50                 try {
 51                     sChannel.close();
 52                     System.out.println("關閉客戶端Socket通道");
 53                 } catch (IOException e) {
 54                     // TODO Auto-generated catch block
 55                     e.printStackTrace();
 56                 }
 57             }
 58         }
 59     }
 60     
 61     
 62     // 啓動非阻塞的NIO的Socket服務器程序
 63     @Test
 64     public void startServer(){
 65         ServerSocketChannel serverSocketChannel = null;
 66         try {
 67             System.out.println("啓動服務器Socket。");
 68             
 69             // --ServerSocketChannel--
 70             // 直接用 服務器Socket通道類 獲取 服務器Socket通道
 71             serverSocketChannel = ServerSocketChannel.open();
 72             // 設置爲非阻塞模式
 73             serverSocketChannel.configureBlocking(false);
 74             // 用 服務器Socket通道 來綁定端口號,端口是須要new的對象
 75             serverSocketChannel.bind(new InetSocketAddress(9898));
 76             
 77             // --Selector--
 78             // 獲取選擇器,選擇器也是經過相似類方法的open方法建立
 79             Selector sel = Selector.open();
 80             
 81             // --註冊--
 82             // 服務器Socket通道註冊選擇器,指定的接收事件
 83             serverSocketChannel.register(sel, SelectionKey.OP_ACCEPT);
 84             
 85             // --開始處理接收到的--
 86             // 輪詢式的獲取選擇器上已經準備就緒的事件
 87             while (sel.select() > 0) {
 88                 // 使用迭代器獲取全部的 "已經接收準備就緒的" 選擇鍵
 89                 Iterator<SelectionKey> it = sel.selectedKeys().iterator();
 90                 
 91                 while (it.hasNext()) {
 92                     SelectionKey selKey = it.next();
 93                     // 若是是接收就緒
 94                     if (selKey.isAcceptable()) {
 95                         System.out.println("接收就緒,接收客戶端的Socket通道。");
 96                         // 經過服務器Socket通道獲取客戶端的鏈接的Socket通道
 97                         SocketChannel socketChannel = serverSocketChannel.accept();
 98                         // 設置這個客戶端發過來的通道爲非阻塞模式
 99                         socketChannel.configureBlocking(false);
100                         // 再將這個客戶端發過來的通道註冊選擇器,由於已經準備好了,那就註冊設置爲讀就緒狀態
101                         socketChannel.register(sel, SelectionKey.OP_READ);
102                         
103                         // 若是是讀就緒狀態
104                     }else if (selKey.isReadable()) {
105 //                        System.out.println("讀就緒,開始讀取客戶端的Socket通道里的數據。");
106                         // 經過選擇器獲取這個通道
107                         SocketChannel socketChannel = (SocketChannel) selKey.channel();
108                         
109                         // 讀取數據
110                         // 建立一個ByteBuffer
111                         ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
112                         
113                         // 從通道中讀取數據到字節緩衝
114                         int len;
115                         while ((len = socketChannel.read(byteBuffer))>0) {
116                             byteBuffer.flip();
117                             System.out.println("開始讀取的數據是:");
118                             System.out.println(new String(byteBuffer.array(),0,len));
119                             byteBuffer.clear();
120                         }
121                         
122                     }
123                     // 記得用完it以後要取消掉
124                     it.remove();
125                 }
126             }
127             
128             
129         } catch (IOException e) {
130             // TODO Auto-generated catch block
131             e.printStackTrace();
132         }finally{
133             try {
134                 if (serverSocketChannel != null) {
135                     serverSocketChannel.close();
136                 }
137             } catch (IOException e) {
138                 // TODO Auto-generated catch block
139                 e.printStackTrace();
140             }
141         }
142     }
143     
144     
145 }
相關文章
相關標籤/搜索