Java之NIO

想要学习Java的Socket通信,首先要学习Java的IO和NIO基础,这方面可以阅读《Java NIO 系列教程》。  

下面展示自己代码熟悉Java的NIO编程的笔记。

 

1、缓冲区(Buffer)

/*
 * 一、缓冲区(Buffer):在Java 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据
 *       
 *       根据数据类型不同(boolean除外),提供了相应类型的缓冲区
 *         ByteBuffer 那么实际上最常用的,因为网络传输的字节就是Byte
 *       CharBuffer
 *         ShortBuffer
 *         IntBuffer
 *         LongBuffer
 *       FloatBuffer
 *       DoubleBuffer
 *     
 *    上述缓冲区的管理方式几乎一致,通过allocate()获取缓冲区
 *    
 *  二、缓冲区存储数据的两个核心方法
 *      put()  : 存入数据到缓冲区中
 *      get()  : 获取缓冲区中的数据
 *      
 *  三、缓冲区中的四个核心属性
 *      capacity : 容量,表示缓冲区中最大存储数据的容量
 *      limit    : 界限,标识缓冲区中可以操作数据的大小(limit后面的数据不能进行读写的)
 *      position : 位置,标识缓冲区中正在操作数据的位置
 *      
 *      mark     : 标记,标识记录当前position的位置,可以通过reset()恢复到mark的位置
 *  
 *      0 <= mark <= position <= limit <= capacity
 *  四、直接缓冲区和非直接缓冲区
 *      非直接缓冲区: 通过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         // 1、分配一个指定大小的缓冲区
 17         ByteBuffer buf = ByteBuffer.allocate(1024);
 18         
 19         // 2、利用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         // 1、分配一个指定大小的缓冲区
 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         // 2、利用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         // 3、前面是存入数据的模式,存入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         // 4、利用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         // 5、可重复读取
 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         // 6、清空缓冲区。但是缓冲区中的数据依然存在,但是出于“被遗忘”状态
 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 }

 2、通道(Channel)

由java.nio.channels包定义的。Channel表示IO源与目标打开的连接。Channel类似于传统的“流”。只不过Channel本身不能直接访问数据,Channel只能与Buffer进行交互。

先完成文件的复制:

  1 /*
  2  * 一、通道(Channel):用于源节点与目标节点的连接。在Java NIO中负责缓冲区中数据的传输。
  3  *                      Channel本身不存储数据,因此需要配合缓冲区进行传输
  4  * 
  5  * 二、通道的主要实现类
  6  *         java.nio.channels.Channel接口
  7  *             |--FileChannel
  8  *             |--SocketChannel
  9  *          |--ServerSocketChannel
 10  *            |--DatagramChannel
 11  *    
 12  * 三、获取通道
 13  *         1、Java针对支持通道的类提供了getChannel()方法
 14  *             本地:
 15  *                 FileInputStream/FileOutputStream
 16  *                 RandomAccessFile随机存储文件流
 17  *             网络:
 18  *                 Socket
 19  *                 ServerSocket
 20  *                 DatagramSocket
 21  * 
 22  *         2、在JDK1.7中的NIO.2针对各个通道提供了一个静态的方法
 23  *         3、在JDK1.7中的NIO.2的Files工具类的newByteChannel()
 24  *             
 25  * 四、通道之间的数据传输
 26  *         transferFrom()
 27  *         transferTo()
 28  * 
 29  * 五、分散(Scatter)和聚集(Gather)
 30  *         分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
 31  *         聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
 32  */
 33 public class TestChannel {
 34     // 1、利用通道完成文件的复制(非直接缓冲区)
 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("1、耗费的时间:"+(end - start));
 97         
 98     }
 99     
100     // 2、利用通道完成文件的复制(直接缓冲区,内存映射文件的方式)
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("2、耗费的时间:"+(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 }

>>1、字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则Java虚拟机会尽最大努力直接在 此缓冲区上执行本机 I/O 操作。也就是说,在每次调用基础操作系统的一个本机 I/O 操作之前(或之后), 虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。


>>2、直接字节缓冲区可以通过调用此类的allocateDirect()工厂方法来创建。此方法返回的缓冲区进行分配和取消 分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对 应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的 本机 I/O 操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好 处时分配它们。


>>3、直接字节缓冲区还可以通过FileChannel的map()方法将文件区域直接映射到内存中来创建。该方法返回 MappedByteBuffer 。Java 平台的实现有助于通过 JNI 从本机代码创建直接字节缓冲区。如果以上这些缓冲区 中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在 访问期间或稍后的某个时间导致抛出不确定的异常。


>>4、字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其isDirect()方法来确定。提供此方法是为了能够在 性能关键型代码中执行显式缓冲区管理。

 

3、字符集(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     }

 4、使用NIO完成网络通信

 1 /*
 2  * 一、使用NIO完成网络通信的三个核心:
 3  *         1、通道(Channel):负责连接
 4  *             java.nio.channels.Channel 接口:
 5  *                 |--SelectableChannel
 6  *                 |--ServerSocketChannel
 7  *                 |--DatagramChannel
 8  * 
 9  *                 |--Pipe.SinkChannel
10  *                 |--Pipe.SourceChannel
11  * 
12  *         2、缓冲区(Buffer):负责数据的存取
13  *     
14  *         3、选择器(Selector):是SelectableChannel的多路复合器。
15  *                             用于监控SelectableChannel的IO状况
16  */

4-1、下面先实现阻塞式通道

 1 public class TestBlockingNIO {
 2     // 客户端
 3     @Test
 4     public void client(){
 5         try {
 6             System.out.println("客户端开始创建。");
 7             // 1、获取Socket通道
 8             SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
 9             // 2、分配指定大小的缓冲区
10             ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
11             // 3、读取本地文件,并发到服务端
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             // 4、客户端发送完数据之后要停止通道的输出
24             socketChannel.shutdownOutput();
25             // --发送完了图片数据之后才会进行下面一步,其实这里也是单线程阻塞的。--
26             System.out.println("--发送完了图片数据之后才会进行下面一步,其实这里也是单线程阻塞的。--");
27             
28             // 5、接收服务端的反馈
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             // 6、关闭通道
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             // 1、获取服务端SocketChannel
54             ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
55             // 2、绑定连接端口号
56             serverSocketChannel.bind(new InetSocketAddress(9898));
57             // 3、获取客户端连接的通道
58             SocketChannel sChannel = serverSocketChannel.accept();
59             
60             // 4、创建一个指定大小的缓冲区
61             ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
62             
63             // 5、接收的数据通道也要文件通道 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             // 6、发送反馈给客户端
77             byteBuffer.put("服务器接收数据成功".getBytes());
78             byteBuffer.flip();
79             sChannel.write(byteBuffer);
80             
81             // 7、关闭通道
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-2、然后是实现非阻塞通道

  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 }

 

posted @ 2017-02-25 18:23  何杨  阅读(646)  评论(0编辑  收藏  举报