zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    Hdfs详解
    HDFS主要特性和体系结构
    HDFS简单入门
    使用docker搭建hadoop分布式集群
    HDFS理论及安装部署
    Android开发之模拟器genymotion安装apk出现错误: Install_failed_invalid_URI
    Android开发之最火的开源框架之一Xutils2详解(摘自开源作者官方介绍详解)
    Java开发之javaEE(java2EE)的介绍,java软件工程师初步阶段知识
    拾色器,可以取出电脑屏幕的任何颜色,ui以及程序员前端等常用软件,文件很小,300K
    Android开发必有功能,更新版本提示,检测是否有新版本更新。下载完成后进行安装。
  • 原文地址:https://www.cnblogs.com/goodboy-heyang/p/6442402.html
Copyright © 2011-2022 走看看