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 }
  • 相关阅读:
    POJ 2175 Evacuation Plan 费用流 负圈定理
    POJ 2983 Is the Information Reliable? 差分约束
    codeforces 420B Online Meeting
    POJ 3181 Dollar Dayz DP
    POJ Ant Counting DP
    POJ 1742 Coins DP 01背包
    中国儒学史
    产品思维30讲
    Java多线程编程核心技术
    编写高质量代码:改善Java程序的151个建议
  • 原文地址:https://www.cnblogs.com/JoePotter/p/13973430.html
Copyright © 2011-2022 走看看