想要學習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 }