Java NIO 学习笔记(四)----文件通道和网络通道
目录:
Java NIO 学习笔记(一)----概述,Channel/Buffer
Java NIO 学习笔记(二)----聚集和分散,通道到通道
Java NIO 学习笔记(三)----Selector
Java NIO 学习笔记(四)----文件通道和网络通道
Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe
Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel
Java NIO 学习笔记(七)----NIO/IO 的对比和总结
FileChannel 文件通道
FileChannel 是连接到文件的通道,可以从文件中读取数据,并将数据写入文件,可以替代使用标准 IO 读写文件的操作。
注意 FileChannel 无法设置为非阻塞模式。 它始终以阻塞模式运行。
打开 FileChannel
在使用 FileChannel 之前必须先将其打开。 无法直接打开 FileChannel ,必须通过 InputStream,OutputStream 或 RandomAccessFile 获取 FileChannel 。 以下是通过 RandomAccessFile 打开 FileChannel 的方法:
RandomAccessFile aFile = new RandomAccessFile("D:\\test\\input.txt", "rw");
FileChannel inChannel = aFile.getChannel();
从 FileChannel 读写数据
这是通过调用 read()/write() 方法之一完成的,读和写都是针对一个 ByteBuufer 对象的。
这是一个例子:
// 这里省略 2 个 Channel 的定义...
ByteBuffer buffer = ByteBuffer.allocate(48);
buffer.clear(); // 清除缓冲区,准备写入数据
int bytesRead = inChannel.read(buffer); // 将 inChannel 的数据读入缓冲区
buffer.flip(); // 反转缓冲区,就是把指针放到开头,并设置 limit 标记结尾
while(buffer.hasRemaining()) { // 只要缓冲区还有数据
outChannel.write(buffer); // 就将缓冲区数据写入通道
}
分配缓冲区后,调用 FileChannel 对象的 read() 方法将 FileChannel 中的数据读入 Buffer ,返回的 int 代表读取的字节数。 如果返回 -1,则到达文件结尾。
使用 write() 方法将数据写入 FileChannel ,该方法同样将 Buffer 作为参数,注意在 while 循环中调用 write() 方法。 这是因为无法保证 write() 方法写入 FileChannel 的字节数。 因此,我们重复调用 write() 方法,直到 Buffer 中没有要写入通道的字节。
关闭 FileChannel
使用 FileChannel 后,必须将其关闭:
channel.close();
FileChannel Position 指定操作位置
可以调用 position() 方法获取 FileChannel 对象的当前位置,调用 position(long pos) 方法来设置 FileChannel 的位置,这样就可以在特定位置开始读取或写入 FileChannel 。
一个例子:
// 获取当前位置
long pos = channel.position();
// 指定通道的位置,0 <= position <= limit
channel.position(pos +123);
如果位置设置在文件结束后面:
- 读取操作将得到 -1 ---- 文件结束标记。
- 写入操作,文件将扩大到指定位置并写入数据。 这可能导致“文件漏洞”,磁盘上的物理文件数据中存在间隙。(比如开始顺序写入123后,此时 position=3,手动指定 position = 6,写入4,那此时磁盘物理文件应该是['1', '2', '3', '~', '~', '~', '4'],中间有3个位置是空的)
获取 FileChannel 的大小信息
FileChannel 对象的 size() 方法返回通道所连接文件的文件大小。 这是一个简单的例子:
long fileSize = channel.size();
FileChannel Truncate(截断)
可以通过调用 FileChannel 对象的 truncate() 方法截断文件。 截断文件时,会以给定长度将其剪切掉,即指定长度后面部分被删除。 这是一个例子,将文件长度截断为 1024 字节:
channel.truncate(1024);
将 FileChannel 数据强制保存到磁盘
FileChannel 对象的 void force(boolean metaData) 方法强制将所有未写入磁盘的数据从通道刷新到磁盘。 出于性能原因,操作系统可能会将数据缓存在内存中,因此在调用 force() 方法之前,无法保证写入通道的数据实际写入磁盘。
boolean 参数指定是否应该刷新文件元数据(权限信息等)到磁盘。
channel.force(true);
channel.force(flase);
SocketChannel 套接字通道
SocketChannel 是连接到 TCP 网络套接字的通道,相当于 Java 网络编程的套接字。可以通过两种方式创建:
- 打开 SocketChannel 并连接到 Internet 上的某个服务器,即向服务器发出连接。
- 当传入连接到达 ServerSocketChannel 时,创建 SocketChannel ,即接收客户端发来的连接。
打开和关闭 SocketChannel
以下是打开SocketChannel的方法:
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://baidu.com", 80));
socketChannel.close();
InetSocketAddress 是 SocketAddress 类的子类,为(IP地址+端口号)类型,也就是端口地址类型,可以使用静态方法 createUnresolved(String host, int port) 获取对象,另外也能由构造函数 InetSocketAddress(InetAddress addr, int port) 创建,其中 InetAddress 对象可省略,也可用字符串代替。
通过 SocketChannel 读写数据
// 这里省略 2 个 Channel 的定义...
ByteBuffer buffer = ByteBuffer.allocate(48);
buffer.clear(); // 清除缓冲区,准备写入数据
int bytesRead = socketChannel.read(buffer); // 将 socketChannel 的数据读入缓冲区
buffer.flip(); // 反转缓冲区,就是把指针放到开头,并设置 limit 标记结尾
while(buffer.hasRemaining()) { // 只要缓冲区还有数据
channel.write(buffer);// 就将缓冲区数据写入通道
}
可以看到,基本和 FileChannel 的读写方式一致,首先分配缓冲区,然后调用 read() 方法。 此方法将数据从 SocketChannel 读入 Buffer 。 read() 方法返回的 int 代表写入了多少字节数据。 如果返回 -1 ,则到达流的末尾(连接已关闭)。调用 SocketChannel 对象的 write(Buffer buffer) 方法则可以将 Buffer 的数据写入 SocketChannel。
SocketChannel 的非阻塞模式
可以将 SocketChannel 设置为非阻塞模式,可以在异步模式下调用 connect(),read() 和 write() 方法。
connect()
如果 SocketChannel 处于非阻塞模式调用 connect() 方法,则可能会在建立连接之前返回。 要确定是否成功建立了连接,可以调用 finishConnect() 方法,它当且仅当已连接此通道的套接字时才返回 true 。
如下所示:
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://baidu.com", 80));
while(! socketChannel.finishConnect() ){
//wait, or do something else...
}
write()
在非阻塞模式下,write() 方法可能在没有写入任何内容的情况下返回。 因此,需要在循环中调用 write() 方法。
while(buffer.hasRemaining()) { // 只要缓冲区还有数据
channel.write(buffer);// 就将缓冲区数据写入通道
}
read()
在非阻塞模式下,read() 方法可能在没有读取任何数据的情况下返回。 因此,需要注意返回的 int ,它代表读取了多少字节。
带有选择器的非阻塞模式
使用 Selector 时,SocketChannel 的非阻塞模式效果更好。 通过使用选择器注册一个或多个 SocketChannel ,可以向选择器询问已准备好进行读取,写入的通道。后面会有更详细的提起,这里先不讲。
ServerSocketChannel
ServerSocketChannel 是一个可以侦听传入 TCP 连接的通道,就像标准 ServerSocket 一样。 这是一个例子:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
while (true) {
SocketChannel socketChannel = serverSocketChannel.accept(); // 监听传入的连接
//do something with socketChannel...
}
serverSocketChannel.close();
ServerSocketChannel 可以设置为非阻塞模式。 在非阻塞模式下,accept() 方法调用后会立即返回,如果有连接传入,则返回 SocketChannel 对象,如果没有连接传入,则返回 null。 因此,必须检查返回的 SocketChannel 是否为空。如下:
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
while (true) {
SocketChannel socketChannel = serverSocketChannel.accept(); // 监听传入的连接
if(socketChannel != null){
//do something with socketChannel...
}
}
DatagramChannel 数据包通道
DatagramChannel 是可以发送和接收 UDP 数据包的通道。 由于 UDP 是一种无连接的网络协议,因此不能像在其他通道中那样读取和写入 DatagramChannel 。 而是通过发送和接收数据包的方式通信。
接收数据
DatagramChannel 是通过 receive() 方法接收数据的。 receive() 方法将接收到的数据包的内容复制到给定的 Buffer 中。 如果接收的数据包包含的数据多于缓冲区可以包含的数据,则会悄悄丢弃多出的数据。一个示例如下:
ByteBuffer receiveBuffer = ByteBuffer.allocate(128);
DatagramChannel serverChannel = DatagramChannel.open();
serverChannel.socket().bind(new InetSocketAddress(9999));
receiveBuffer.clear(); // 清除缓冲区,准备写入数据
serverChannel.receive(receiveBuffer);
receiveBuffer.flip(); // 反转缓冲区以准备被读取
System.out.println(new String(receiveBuffer.array(), 0, receiveBuffer.limit()));
发送数据
ByteBuffer sendBuffer = ByteBuffer.allocate(128);
sendBuffer.clear(); // 清除缓冲区,准备写入数据
byte[] sendData = "string from cilent".getBytes();
sendBuffer.put(sendData);
DatagramChannel clientChannel = DatagramChannel.open();
int sendSuccess = clientChannel.send(sendBuffer, new InetSocketAddress("127.0.0.1", 9999));
System.out.println("sendSuccess: " + sendSuccess);
clientChannel.close();
此示例将字符串发送到 UDP 本机的 9999 端口, 由于 UDP 不对数据传送做出任何保证,因此不会通知对方是否收到了发送的数据包。
连接到特定地址
可以将 DatagramChannel“连接”到网络上的特定地址。 由于 UDP 是无连接的,因此这种连接到地址的方式不会像 TCP 通道那样创建真正的连接。 它只会锁定 DatagramChannel ,让其只能从一个特定地址发送和接收数据包。一个示例:
channel.connect(new InetSocketAddress("baidu.com", 80));
连接后,还可以使用 read() 和 write() 方法,就像使用传统通道一样,只是在数据传送方面没有任何保证。 例子:
int bytesRead = channel.read(buf);
int bytesWritten = channel.write(buf);
Java NIO 学习笔记(四)----文件通道和网络通道的更多相关文章
- 零拷贝详解 Java NIO学习笔记四(零拷贝详解)
转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...
- Java NIO学习笔记四 NIO选择器
Java NIO选择器 A Selector是一个Java NIO组件,可以检查一个或多个NIO通道,并确定哪些通道已准备就绪,例如读取或写入.这样一个线程可以管理多个通道,从而管理多个网络连接. 为 ...
- Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(五)----路径、文件和管道 Path/Files/Pipe
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(二)----聚集和分散,通道到通道
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO学习笔记
Java NIO学习笔记 一 基本概念 IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. 所有语言运行时系统提供执 ...
- Java NIO 学习笔记(七)----NIO/IO 的对比和总结
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(三)----Selector
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(一)----概述,Channel/Buffer
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
随机推荐
- Method of Seamless Integration and Independent Evolution of Information-Centric Networking via Software Defined Networking
A method of transferring data between a software defined network (SDN) and an information-centric ne ...
- C# WPF 歌词控件(支持逐字定位描色效果)
原文:C# WPF 歌词控件(支持逐字定位描色效果) 之前做了一个模仿网易云歌词的控件,实现了加载网易云歌词并能随音乐播放进度定位歌词.今天呢将在这个控件的基础上增加逐字定位描色功能,如下图效果(QQ ...
- DDD实战2 创建领域基础类库项目
1.创建领域基础类库项目 取名DDD.DomainBase 放置于基础设施层 2.新建IEntity接口,内部创建2个属性 code和Id用于限定 public interface IEntity { ...
- WPF模拟Office2010文件菜单的TabControl模板
原文:WPF模拟Office2010文件菜单的TabControl模板 这是Office2010中的文件菜单点开后的效果.本文我将以强大的WPF(www.itstrike.cn)来实现类似的效果.希望 ...
- VC++ 编译libcurl 支持SSL,GZIP(有脚本)
由于网上下载的 libcurl 不支持 gzip,只好自己动手编译,期间走了很多弯路,下面是最终成功的记录. 我所使用的环境 Visual Studio 2010 . Windows 7 64 bit ...
- Qt调用PolarSSL库(一个)
最近一直在学习SSL相关知识,也明白了理论相关知识,主要SSL基本概念和连接建立.主要依据PolarSSL开源库学习.学习完了之后就希望能给有所运用,就想用Qt写一个简单的程序,添加对SSL相关概念的 ...
- 关闭Mac OS 的Rootless
今天在使用mac的时候,需要删除 /usr/bin/下的 自带的php文件.然后提示Operation not permitted 使用sudo 依然不可以,通过google 得到解决方案. 需要关闭 ...
- GlyphRun 对象和 Glyphs 元素简介
原文 GlyphRun 对象和 Glyphs 元素简介 GlyphRun 简介 Windows Presentation Foundation (WPF) 提供高级的文本支持包括直接访问的标志符号级标 ...
- 用树莓派和DS18B20做个汽车温度记录仪
原文:用树莓派和DS18B20做个汽车温度记录仪 用树莓派和DS18B20做个汽车温度记录仪[原创] 很想知道夏日阳光暴晒下,汽车内的最高温度以及温度的变化情况.觉得用树莓派和DS18B20来实现应该 ...
- MVC 组件之间的关系
View和Controller都可以直接请求Model 但是Model不依赖View和controller lController可以直接请求View来显示具体页面 View不依赖Controller ...