zoukankan      html  css  js  c++  java
  • Java NIO系列使用示例

      1 package com.nio.test;
      2 
      3 import java.io.IOException;
      4 import java.io.RandomAccessFile;
      5 import java.net.InetSocketAddress;
      6 import java.nio.ByteBuffer;
      7 import java.nio.CharBuffer;
      8 import java.nio.channels.DatagramChannel;
      9 import java.nio.channels.FileChannel;
     10 import java.nio.channels.Pipe;
     11 import java.nio.channels.ServerSocketChannel;
     12 import java.nio.channels.SocketChannel;
     13 import java.nio.charset.Charset;
     14 import java.nio.charset.CharsetDecoder;
     15 import java.nio.charset.CoderResult;
     16 import java.nio.file.Files;
     17 import java.nio.file.LinkOption;
     18 import java.nio.file.Path;
     19 import java.nio.file.Paths;
     20 
     21 public class ChannelTest {
     22     public static void main(String[] args) throws Exception {
     23         new ChannelTest().filewrite();
     24         new ChannelTest().byteBufferUtf8();
     25         new ChannelTest().fileread();
     26         new ChannelTest().clientsocket();
     27         new ChannelTest().serverSocket();
     28         new ChannelTest().serverDatagram();
     29         new ChannelTest().clientDatagram();
     30         new ChannelTest().pipe();
     31         new ChannelTest().NIOPath();
     32     }
     33 
     34     private void fileread() {
     35         RandomAccessFile aFile;
     36         Charset charset = Charset.forName("UTF-8");
     37         CharsetDecoder decoder = charset.newDecoder();
     38         try {
     39             // 在使用FileChannel之前,必须先打开它。但是,我们无法直接打开一个FileChannel,
     40             // 需要通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例
     41             aFile = new RandomAccessFile("src/com/nio/test/nio-data.txt", "rw");
     42 
     43             FileChannel inChannel = aFile.getChannel();
     44             // 首先,分配一个Buffer。从FileChannel中读取的数据将被读到Buffer中。
     45             // create buffer with capacity of 48 byte
     46             ByteBuffer byteBuffer = ByteBuffer.allocate(48);// read into buffer.
     47             CharBuffer charBuffer = CharBuffer.allocate(48);
     48 
     49             // 调用多个read()方法之一 从FileChannel中读取数据。
     50             int bytesRead = inChannel.read(byteBuffer);
     51 
     52             char[] tmp = null; // 临时存放转码后的字符
     53             byte[] remainByte = null;// 存放decode操作后未处理完的字节。decode仅仅转码尽可能多的字节,此次转码不了的字节需要缓存,下次再转
     54             int leftNum = 0; // 未转码的字节数
     55             
     56             while (bytesRead != -1) {
     57 
     58                 //System.out.println("Read " + bytesRead);
     59                 byteBuffer.flip(); // make buffer ready for read
     60                 decoder.decode(byteBuffer, charBuffer, false);
     61                 
     62                 charBuffer.flip();
     63 
     64                 remainByte = null;
     65                 leftNum = byteBuffer.limit() - byteBuffer.position();
     66                 if (leftNum > 0) { // 记录未转换完的字节
     67                     remainByte = new byte[leftNum];
     68                     byteBuffer.get(remainByte, 0, leftNum);
     69                 }
     70                 
     71                 // 输出已转换的字符
     72                 tmp = new char[charBuffer.length()];
     73                 while (charBuffer.hasRemaining()) {
     74                     charBuffer.get(tmp);
     75                     System.out.print(new String(tmp));
     76                 }
     77                 
     78                 byteBuffer.clear(); // make buffer ready for writing
     79                 charBuffer.clear();
     80 
     81                 if (remainByte != null) {
     82                     byteBuffer.put(remainByte); // 将未转换完的字节写入bBuf,与下次读取的byte一起转换
     83                 }
     84                 
     85                 bytesRead = inChannel.read(byteBuffer);
     86             }
     87             
     88             aFile.close();
     89         } catch (Exception e) {
     90             // TODO Auto-generated catch block
     91             e.printStackTrace();
     92         }
     93     }
     94 
     95     private void filewrite() throws Exception {
     96         RandomAccessFile accessFile = new RandomAccessFile("src/com/nio/test/nio-data11.txt", "rw");
     97         FileChannel fileChannel = accessFile.getChannel();
     98         String newDate = "New String to write to file" + System.currentTimeMillis();
     99         ByteBuffer buffer = ByteBuffer.allocate(48);
    100         buffer.clear();
    101         buffer.put(newDate.getBytes());
    102         buffer.flip();
    103         while (buffer.hasRemaining()) {
    104             fileChannel.write(buffer);
    105         }
    106 
    107         /**
    108          * FileChannel的truncate方法
    109          * 可以使用FileChannel.truncate()方法截取一个文件。截取文件时,文件将中指定长度后面的部分将被删除。如:
    110          * 
    111          * 1 channel.truncate(1024); 这个例子截取文件的前1024个字节。
    112          */
    113         //fileChannel.truncate(12);
    114         /**
    115          * FileChannel.force()方法将通道里尚未写入磁盘的数据制写到磁盘上。出于性能方面的考虑,操作系统会将数据缓存在内存中,
    116          * 所以无法保证写入到FileChannel里的数据一定会即时写到磁盘上。要保证这一点,需要调用force()方法。
    117          */
    118         fileChannel.force(true);// force()方法有一个boolean类型的参数,指明是否同时将文件元数据(权限信息等)写到磁盘上。
    119 
    120         fileChannel.close();
    121     }
    122 
    123     private void clientsocket() throws Exception {
    124         SocketChannel socketChannel = SocketChannel.open();
    125         //可以设置 SocketChannel 为非阻塞模式(non-blocking mode).设置之后,就可以在异步模式下调用connect(), read() 和write()了。
    126         socketChannel.configureBlocking(false);
    127         socketChannel.connect(new InetSocketAddress("127.0.0.1", 60000));
    128         
    129         //为非阻塞模式的判断用
    130         while(!socketChannel.finishConnect()){
    131             socketChannelRead(socketChannel);
    132         }
    133     }
    134     
    135     /**
    136      * 
    137      * ServerSocketChannel
    138      * 
    139      * Java NIO中的 ServerSocketChannel 是一个可以监听新进来的TCP连接的通道, 
    140      * 就像标准IO中的ServerSocket一样。ServerSocketChannel类在 java.nio.channels包中。
    141      * 
    142      * @throws Exception 
    143      * 
    144      */
    145     private void serverSocket() throws Exception {
    146         // 通过调用 ServerSocketChannel.open() 方法来打开ServerSocketChannel.
    147         ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    148         serverSocketChannel.socket().bind(new InetSocketAddress(60000));
    149         // ServerSocketChannel可以设置成非阻塞模式。在非阻塞模式下,accept()
    150         // 方法会立刻返回,如果还没有新进来的连接,返回的将是null。
    151         // 因此,需要检查返回的SocketChannel是否是null.
    152         serverSocketChannel.configureBlocking(false);
    153         // 通常不会仅仅只监听一个连接,在while循环中调用 accept()方法.
    154         while (true) {
    155             // 监听新进来的连接
    156             // 通过 ServerSocketChannel.accept() 方法监听新进来的连接。当
    157             // accept()方法返回的时候,它返回一个包含新进来的连接的 SocketChannel。
    158             // 因此, accept()方法会一直阻塞到有新连接到达。
    159             SocketChannel socketChannel = serverSocketChannel.accept();
    160 
    161             // 非阻塞模式
    162             if (socketChannel != null) {
    163                 socketChannelRead(socketChannel);
    164             }
    165             //通过调用ServerSocketChannel.close() 方法来关闭ServerSocketChannel
    166             // serverSocketChannel.close();
    167         }
    168     }
    169     
    170     private static StringBuilder socketChannelRead(SocketChannel socketChannel) throws Exception {
    171         
    172         StringBuilder sb = new StringBuilder();
    173         
    174         Charset charset = Charset.forName("GBK");
    175         CharsetDecoder decoder = charset.newDecoder();
    176 
    177         ByteBuffer byteBuffer = ByteBuffer.allocate(10);
    178         CharBuffer charBuffer = CharBuffer.allocate(10);
    179 
    180         int bytesRead = socketChannel.read(byteBuffer);
    181 
    182         char[] tmp = null; // 临时存放转码后的字符
    183         byte[] remainByte = null;// 存放decode操作后未处理完的字节。decode仅仅转码尽可能多的字节,此次转码不了的字节需要缓存,下次再转
    184         int leftNum = 0; // 未转码的字节数
    185 
    186         while (bytesRead != -1) {
    187 
    188             // System.out.println("Read " + bytesRead);
    189             byteBuffer.flip(); // make buffer ready for read
    190             CoderResult result = decoder.decode(byteBuffer, charBuffer, false);
    191 //            System.out.println("result:"+ result);
    192             charBuffer.flip();
    193 
    194             remainByte = null;
    195             leftNum = byteBuffer.limit() - byteBuffer.position();
    196             if (leftNum > 0) { // 记录未转换完的字节
    197                 remainByte = new byte[leftNum];
    198                 byteBuffer.get(remainByte, 0, leftNum);
    199             }
    200 
    201             // 输出已转换的字符
    202             tmp = new char[charBuffer.length()];
    203             while (charBuffer.hasRemaining()) {
    204                 charBuffer.get(tmp);
    205                 //sb.append(tmp);
    206                 System.out.print(new String(tmp));
    207             }
    208 
    209             byteBuffer.clear(); // make buffer ready for writing
    210             charBuffer.clear();
    211 
    212             if (remainByte != null) {
    213                 byteBuffer.put(remainByte); // 将未转换完的字节写入bBuf,与下次读取的byte一起转换
    214             }
    215             bytesRead = socketChannel.read(byteBuffer);
    216         }
    217         return sb;
    218     }
    219     
    220     /**
    221      * Java NIO中的DatagramChannel是一个能收发UDP包的通道。
    222      * 因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。
    223      * 
    224      * @throws Exception
    225      */
    226     private void serverDatagram() throws Exception {
    227         /**
    228          * 这个例子打开的 DatagramChannel可以在UDP端口9999上接收数据包。
    229          */
    230         DatagramChannel channel = DatagramChannel.open();
    231         channel.socket().bind(new InetSocketAddress(60000));
    232         
    233         Charset charset = Charset.forName("GBK");
    234         CharsetDecoder decoder = charset.newDecoder();
    235         
    236         //通过receive()方法从DatagramChannel接收数据
    237         //receive()方法会将接收到的数据包内容复制到指定的Buffer.
    238         //如果Buffer容不下收到的数据,多出的数据将被丢弃。
    239         ByteBuffer byteBuffer = ByteBuffer.allocate(48);
    240         CharBuffer charBuffer = CharBuffer.allocate(48);
    241         byteBuffer.clear();
    242         channel.receive(byteBuffer);
    243         
    244         char[] tmp = null; // 临时存放转码后的字符
    245         while(true){
    246             byteBuffer.flip();
    247             
    248             CoderResult result = decoder.decode(byteBuffer, charBuffer, false);
    249             
    250             charBuffer.flip();
    251             tmp = new char[charBuffer.length()];
    252             while (charBuffer.hasRemaining()) {
    253                 charBuffer.get(tmp);
    254                 System.out.print(new String(tmp));
    255             }
    256             byteBuffer.clear();
    257             charBuffer.clear();
    258             channel.receive(byteBuffer);
    259         }
    260     }
    261     /**
    262      * 可以将DatagramChannel“连接”到网络中的特定地址的。由于UDP是无连接的,
    263      * 连接到特定地址并不会像TCP通道那样创建一个真正的连接。
    264      * 而是锁住DatagramChannel ,让其只能从特定地址收发数据。
    265      * 当连接后,也可以使用read()和write()方法,就像在用传统的通道一样。只是在数据传送方面没有任何保证。
    266      * 
    267      * @throws Exception
    268      */
    269     private void clientDatagram() throws Exception {
    270         DatagramChannel channel = DatagramChannel.open();
    271         String newData = "New^啊&ng& to write to fasdfsdafsdfdsfsadf1JLKJL)(&)&*(&&ile..." + System.currentTimeMillis();
    272         ByteBuffer buf = ByteBuffer.allocate(480);
    273         buf.clear();
    274         buf.put(newData.getBytes("GBK"));
    275         buf.flip();
    276         
    277         //通过send()方法从DatagramChannel发送数据  即使下面的地址无法连接也是可以发送数据的。
    278         int bytesSent = channel.send(buf, new InetSocketAddress("127.0.0.1", 60000));
    279         //UDP在数据传送方面没有任何保证。
    280     }
    281     
    282     /**
    283      * Java NIO 管道是2个线程之间的单向数据连接。Pipe有一个source通道和一个sink通道。
    284      * 数据会被写到sink通道,从source通道读取。
    285      * @throws Exception 
    286      */
    287     private void pipe() throws Exception {
    288         
    289         Pipe pipe = Pipe.open();
    290 
    291         //构建一条线程 ,获取管道的SinkChannel,用于数据录入
    292         Thread thread = new Thread(() -> {
    293             // 向管道写数据
    294             // 要向管道写数据,需要访问sink通道。
    295             // 通过调用SinkChannel的write()方法,将数据写入SinkChannel,像这样:
    296             Pipe.SinkChannel sinkChannel = pipe.sink();
    297             String newData = "New String to write to file..." + System.currentTimeMillis();
    298             ByteBuffer buf = ByteBuffer.allocate(48);
    299             buf.clear();
    300             try {
    301                 buf.put(newData.getBytes("GBK"));
    302 
    303                 buf.flip();
    304                 while (buf.hasRemaining()) {
    305                     sinkChannel.write(buf);
    306                 }
    307             } catch (Exception e) {
    308                 // TODO Auto-generated catch block
    309                 e.printStackTrace();
    310             }
    311         });
    312         
    313         //构建一条线程 ,让其去获取到SinkChannel录入的数据并输出
    314         Thread thread1 = new Thread(() -> {
    315             // 从管道读取数据
    316             // 从读取管道的数据,需要访问source通道,
    317             // 调用source通道的read()方法来读取数据,
    318             Pipe.SourceChannel sourceChannel = pipe.source();
    319 
    320             Charset charset = Charset.forName("GBK");
    321             CharsetDecoder decoder = charset.newDecoder();
    322 
    323             ByteBuffer byteBuffer = ByteBuffer.allocate(48);
    324             CharBuffer charBuffer = CharBuffer.allocate(48);
    325 
    326             char[] tmp = null; // 临时存放转码后的字符
    327             byte[] remainByte = null;// 存放decode操作后未处理完的字节。decode仅仅转码尽可能多的字节,此次转码不了的字节需要缓存,下次再转
    328             int leftNum = 0; // 未转码的字节数
    329 
    330             // read()方法返回的int值会告诉我们多少字节被读进了缓冲区。
    331             int bytesRead;
    332             try {
    333                 bytesRead = sourceChannel.read(byteBuffer);
    334 
    335                 while (bytesRead != -1) {
    336 
    337                     // System.out.println("Read " + bytesRead);
    338                     byteBuffer.flip(); // make buffer ready for read
    339                     CoderResult result = decoder.decode(byteBuffer, charBuffer, false);
    340                     // System.out.println("result:"+ result);
    341                     charBuffer.flip();
    342 
    343                     remainByte = null;
    344                     leftNum = byteBuffer.limit() - byteBuffer.position();
    345                     if (leftNum > 0) { // 记录未转换完的字节
    346                         remainByte = new byte[leftNum];
    347                         byteBuffer.get(remainByte, 0, leftNum);
    348                     }
    349 
    350                     // 输出已转换的字符
    351                     tmp = new char[charBuffer.length()];
    352                     while (charBuffer.hasRemaining()) {
    353                         charBuffer.get(tmp);
    354                         // sb.append(tmp);
    355                         System.out.print(new String(tmp));
    356                     }
    357 
    358                     byteBuffer.clear(); // make buffer ready for writing
    359                     charBuffer.clear();
    360 
    361                     if (remainByte != null) {
    362                         byteBuffer.put(remainByte); // 将未转换完的字节写入bBuf,与下次读取的byte一起转换
    363                     }
    364                     bytesRead = sourceChannel.read(byteBuffer);
    365                 }
    366             } catch (IOException e) {
    367                 // TODO Auto-generated catch block
    368                 e.printStackTrace();
    369             }
    370         });
    371         
    372         thread.run();
    373         Thread.sleep(2000L);
    374         thread1.run();
    375         
    376     }
    377     
    378     /**
    379      * Path接口是java NIO2的一部分。首次在java 7中引入。Path接口在java.nio.file包下,
    380      * 所以全称是java.nio.file.Path。 java中的Path表示文件系统的路径。可以指向文件或文件夹。
    381      * 也有相对路径和绝对路径之分。绝对路径表示从文件系统的根路径到文件或是文件夹的路径。
    382      * 相对路径表示从特定路径下访问指定文件或文件夹的路径。相对路径的概念可能有点迷糊,可以自己百度一下。
    383      * 不要将文件系统的path和操作系统的环境变量path搞混淆。java.nio.file.Path接口和操作系统的path环境变量没有任何关系。
    384      * 在很多方面,java.nio.file.Path接口和java.io.File有相似性,但也有一些细微的差别。
    385      * 在很多情况下,可以用Path来代替File类。
    386      */
    387     private void NIOPath() {
    388         //为了使用java.nio.file.Path实例,必须首先创建它。可以使用Paths 类的静态方法Paths.get()来产生一个实例。
    389         //请注意例子开头的两个import语句。想要使用Paths类和Path接口,必须首先引入相应包。
    390         //其次,注意Paths.get(“c:\data\myfile.txt”)的用法。
    391         //其使用了Paths.get方法创建了Path的实例。它是一个工厂方法。
    392         Path path = Paths.get("c:\data\myfile.txt");//绝对路径Path
    393         
    394         
    395         //创建相对路径Path
    396         //java NIO Path类也能使用相对路径。可以通过Paths.get(basePath, relativePath)创建一个相对路径Path。
    397         Path projects = Paths.get("d:\data", "projects");
    398         //创建了一个指向d:dataprojects文件夹的实例。
    399         Path file = Paths.get("d:\data", "projects\a-project\myfile.txt");
    400         //创建了一个指向 d:dataprojectsa-projectmyfile.txt 文件的实例。
    401         //.表示当前路径。例如,如果以如下方式创建一个相对路径:
    402         //创建的Path实例对应的路径就是运行这段代码的项目工程目录。
    403         Path currentDir = Paths.get(".");
    404         System.out.println(currentDir.toAbsolutePath());
    405         //..表示父类目录。
    406         Path parentDir = Paths.get("..");
    407         String path1 = "d:\data\projects\a-project\..\another-project";
    408         Path parentDir2 = Paths.get(path1);
    409         //d:dataprojectsanother-project在a-project目录后面的..符号,
    410         //将指向的目录修改为projects目录,因此,最终path指向another-project目录。
    411         
    412         
    413         //Path 的normalize()方法可以标准化路径。
    414         String originalPath =
    415                  "d:\data\projects\a-project\..\another-project";
    416 
    417         Path path3 = Paths.get(originalPath);
    418         System.out.println("path3 = " + path3);
    419 
    420         Path path2 = path3.normalize();
    421         System.out.println("path2 = " + path2);
    422         //如你所见,标准化后的路径不再包含 a-project..部分,因为它是多余的。
    423         
    424         //Files.exists()
    425         //Files.exists()方法用来检查文件系统中是否存在某路径。
    426         //Path实例对应的路径可能在文件系统中并不存在。例如,如果打算新建一个文件夹,首先需要创建一个对应的Path实例,然后才能创建对应路径下的文件夹。
    427         //因为Path实例对应的路径在文件系统的存在性不确定,可以使用Files.exists()方法确认Path对应的路径是否存在 (也就是开发需要自己显式的去调用该方法确认)。
    428         //如下是Files.exists()的示例:
    429         Path path5 = Paths.get("data/logging.properties");
    430 
    431         boolean pathExists = Files.exists(path, new LinkOption[] { LinkOption.NOFOLLOW_LINKS });
    432         System.out.println(pathExists);
    433         //示例中首先创建了一个Path。然后,通过调用Files.exists方法并将path作为第一个参数确认path对应的路径是否存在。
    434         //注意下Files.exist()方法的第二个参数。第二个参数数组是评判路径是否存在时使用的规则。
    435         //示例中,数组包含LinkOption.NOFOLLOW_LINKS枚举类型,表示Files.exists不会跟进到路径中有连接的下层文件目录。
    436         //表示path路径中如果有连接,Files.exists方法不会跟进到连接中去
    437     }
    438     
    439     private void byteBufferUtf8() throws Exception {
    440         Charset charset = null;
    441         CharsetDecoder decoder = null;
    442         String charsetName = "UTF-8";
    443         int capacity = 10;
    444 
    445         charset = Charset.forName(charsetName);
    446         decoder = charset.newDecoder();
    447 
    448         String s = "客户端发送dsad德生科技电脑fdas上考虑迪士尼年少弗拉门发生ofjam打什么的即破发麦克 ‘;打, 饭哦按asdfasfsdfdfsfdsf都客户端发送dsad德生科技电脑fdas上考虑迪士尼年少弗拉门发生ofjam打什么的即破发麦克 ‘;打, 饭哦按asdfasfsdfdfsfdsf都客户端发送dsad德生科技电脑fdas上考虑迪士尼年少弗拉门发生ofjam打什么的即破发麦克 ‘;打, 饭哦按asdfasfsdfdfsfdsf都客户端发送dsad德生科技电脑fdas上考虑迪士尼年少弗拉门发生ofjam打什么的即破发麦克 ‘;打, 饭哦按asdfasfsdfdfsfdsf都";
    449         byte[] bytes = s.getBytes(charsetName);
    450 
    451         // 模拟接收的ByteBuffer size 10
    452         ByteBuffer byteBuffer = ByteBuffer.allocate(capacity);
    453         // 用于临时存放Bytebuffer转换后的字符
    454         CharBuffer charBuffer = CharBuffer.allocate(capacity);
    455         // 用于连接展示字符串
    456         StringBuilder sb = new StringBuilder();
    457 
    458         int i = 0;
    459         while (true) {
    460             byteBuffer.put(bytes[i]);
    461             i++;
    462             if (byteBuffer.remaining() == 0 || i == bytes.length) {
    463                 byteBuffer.flip();
    464                 CoderResult coderResult;
    465                 if (i != bytes.length) {
    466                     coderResult = decoder.decode(byteBuffer, charBuffer, false);
    467                 } else {
    468                     coderResult = decoder.decode(byteBuffer, charBuffer, true);
    469                 }
    470                 // 有错误
    471                 if (coderResult.isError()) {
    472                     coderResult.throwException();
    473                 }
    474                 charBuffer.flip();
    475                 sb.append(charBuffer);
    476                 charBuffer.clear();
    477                 byteBuffer.compact();
    478             }
    479             // 退出循环
    480             if (i == bytes.length) {
    481                 break;
    482             }
    483         }
    484         System.out.println(sb);
    485     }
    486 }
  • 相关阅读:
    TF400916错误修复办法
    架构优化
    .net 动态代理的泛型方法支持问题
    解决Kafka-1194问题
    SOA服务总线设计
    SOA初探
    重构心法——提高代码复用率
    重构心法——拆分大对象
    重构心法——你应该这样写方法
    反爬虫那些事儿
  • 原文地址:https://www.cnblogs.com/JoePotter/p/13973430.html
Copyright © 2011-2022 走看看