1 packagecom.nio.test;2
3 importjava.io.IOException;4 importjava.io.RandomAccessFile;5 importjava.net.InetSocketAddress;6 importjava.nio.ByteBuffer;7 importjava.nio.CharBuffer;8 importjava.nio.channels.DatagramChannel;9 importjava.nio.channels.FileChannel;10 importjava.nio.channels.Pipe;11 importjava.nio.channels.ServerSocketChannel;12 importjava.nio.channels.SocketChannel;13 importjava.nio.charset.Charset;14 importjava.nio.charset.CharsetDecoder;15 importjava.nio.charset.CoderResult;16 importjava.nio.file.Files;17 importjava.nio.file.LinkOption;18 importjava.nio.file.Path;19 importjava.nio.file.Paths;20
21 public classChannelTest {22 public static void main(String[] args) throwsException {23 newChannelTest().filewrite();24 newChannelTest().byteBufferUtf8();25 newChannelTest().fileread();26 newChannelTest().clientsocket();27 newChannelTest().serverSocket();28 newChannelTest().serverDatagram();29 newChannelTest().clientDatagram();30 newChannelTest().pipe();31 newChannelTest().NIOPath();32 }33
34 private voidfileread() {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(newString(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() throwsException {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() throwsException {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 * ServerSocketChannel138 *139 * Java NIO中的 ServerSocketChannel 是一个可以监听新进来的TCP连接的通道,140 * 就像标准IO中的ServerSocket一样。ServerSocketChannel类在 java.nio.channels包中。141 *142 *@throwsException143 *144 */
145 private void serverSocket() throwsException {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() 方法来关闭ServerSocketChannel166 //serverSocketChannel.close();
167 }168 }169
170 private static StringBuilder socketChannelRead(SocketChannel socketChannel) throwsException {171
172 StringBuilder sb = newStringBuilder();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(newString(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 returnsb;218 }219
220 /**
221 * Java NIO中的DatagramChannel是一个能收发UDP包的通道。222 * 因为UDP是无连接的网络协议,所以不能像其它通道那样读取和写入。它发送和接收的是数据包。223 *224 *@throwsException225 */
226 private void serverDatagram() throwsException {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(newString(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 *@throwsException268 */
269 private void clientDatagram() throwsException {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 *@throwsException286 */
287 private void pipe() throwsException {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 intbytesRead;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(newString(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 voidNIOPath() {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");//绝对路径Path393
394
395 //创建相对路径Path396 //java NIO Path类也能使用相对路径。可以通过Paths.get(basePath, relativePath)创建一个相对路径Path。
397 Path projects = Paths.get("d:\\data", "projects");398 //创建了一个指向d:\data\projects文件夹的实例。
399 Path file = Paths.get("d:\\data", "projects\\a-project\\myfile.txt");400 //创建了一个指向 d:\data\projects\a-project\myfile.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:\data\projects\another-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, newLinkOption[] { 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() throwsException {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 = newStringBuilder();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 }