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:\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, 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 }
java ntohs实现 java nio示例
转载本文章为转载内容,我们尊重原作者对文章享有的著作权。如有内容错误或侵权问题,欢迎原作者联系我们进行内容更正或删除文章。
提问和评论都可以,用心的回复会被更多人看到
评论
发布评论
相关文章
-
Java NIO - IO多路复用详解
本文主要对IO多路复用,Ractor模型以及Java NIO对其的支持。
Java IO/NIO/AIO -
Java NIO系列使用示例
Java NIO
非阻塞IO 并发 Java NIO java 数据