FileChannel
如何从FileChannel中读取数据?
public static void main(String[] args) throws IOException {
//1.创建FileChannel
RandomAccessFile accessFile=new RandomAccessFile("E:\\testData\\FileChannel01.txt","rw");//rw表示的是读和写
FileChannel channel=accessFile.getChannel();
//2.创建Buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
//3.读取数据到buffer中
int read = channel.read(buffer);//返回的值如果是-1就表示已经读到最后一行了
while(read!=-1){
System.out.println("读取了"+read);
//3.1.翻转读写模式
buffer.flip();
//3.2.buffer是否有剩余的内容
while (buffer.hasRemaining()){
System.out.println((char)buffer.get());
}
//3.3.清除buffer
buffer.clear();
//3.4.让他继续往下读
read=channel.read(buffer);
}
accessFile.close();;
System.out.println("操作结束");
}
对以上代码做出解释:
在使用fileChannel之前必须先打开它,但是我们无法直接打开它,所以只能通过RandomAccessFile来获取一个FileChannel实例
RandomAccessFile accessFile=new RandomAccessFile("E:\\testData\\FileChannel01.txt","rw");
FileChannel channel=accessFile.getChannel();
调用多个read方法之一从FileChannel中读取数。首先分配一个buffer,从FileChannel中读取的数据将被读到Buffer中,然后调用read方法,该方法将数据从FileChannel读取到Buffer中
ByteBuffer buffer = ByteBuffer.allocate(1024);
int read = channel.read(buffer);
如何向FileChannel写数据
private static void WriteDataToFileChannel() throws IOException{
//1.打开FileChannel
RandomAccessFile accessFile=new RandomAccessFile("E:\\testData\\FileChannel01.txt","rw");
FileChannel channel = accessFile.getChannel();
//2.创建一个Buffer对象
ByteBuffer buffer=ByteBuffer.allocate(1024);
String newData="wuyimin";
buffer.clear();
//3.写入内容
buffer.put(newData.getBytes());
//3.1转换为读模式
buffer.flip();
//3.2FileChannel完成最终的实现
//必须在循环里调用write方法,因为无法保证一次能写入多少字节
while (buffer.hasRemaining()){
channel.write(buffer);
}
//4.关闭fileChannel
channel.close();
}
FileChannel的position方法
有时候可能需要在FileChannel的某个特定的位置进行数据的读写操作,可以通过调用position方法来获取到FileChannel的当前位置,也可以通过调用position方法设置FileChannel的当前位置:
long pos=channel.position();
channel.position(pos+123);
注意点:
- 如果将位置设置在文件结束符之后,然后试图冲文件通道中获取数据,读方法将返回-1。
- 如果就将位置设置在文件结束符之后,然后向通道中写数据,文件将撑大到当前位置写入数据,这可能导致文件空袭,磁盘上物理文件中写入的数据间有空隙
FileChannel的size方法
返回实例关联文件的大小
FileChannel的truncate方法
截取一个文件,比如channel.truncate(1024),截取前1024个文件
FileChannel的force方法
将通道里尚未写道磁盘的数据强制写到磁盘上,操作系统会将数据缓存在内存中,无法保证写入到FileChannel里的数据一定会及时写在磁盘上,要保证这一点,需要调用force方法。
该方法有个boolean类型的参数,指明是否同时将文件元数据写道磁盘上
FileChannel的transferTo和transferFrom方法
用于通道之间的数据传输,如果to的文件有原有内容会被from的内容覆盖掉
private static void transferFrom() throws IOException {
//创建两个accessFile
RandomAccessFile accessFile1 = new RandomAccessFile("E:\\testData\\FileChannel01.txt", "rw");
RandomAccessFile accessFile2 = new RandomAccessFile("E:\\testData\\FileChannel02.txt", "rw");
FileChannel from = accessFile1.getChannel();
FileChannel to = accessFile2.getChannel();
//我们想把from的数据传输到to里去
long pos=0;
long size=from.size();
//从第0个位置开始传输
to.transferFrom(from,pos,size);
//关闭文件
accessFile1.close();
accessFile2.close();
System.out.println("传输完毕");
}
Socket通道--(Datagram,Socket,ServerSocket)
DaragramChannel和SocketChannel实现定义读和写的功能接口而ServerSocketChannel不实现,ServerSocketChannel负责监听传入的连接和创建新的SocketChannel对象,它本身从不传输数据
socket和socket通道之间的关系:通道是一个连接IO服务导管并且提供与该服务交互的方法,就某个socket而言,它不会再次实现与之对应的socket通道类中的socket协议api,已经存在的socket通道可以被大多数协议操作重复使用
ServerSocketChannel
它是一个基于通道的socket监听器
通过bind方法绑定一个socket
ServerSocketChannel的accept方法会返回一个SocketChannel对象,SocketChannel可以在非阻塞模式下运行
private static void ServerSocketChannelTest() throws IOException, InterruptedException {
//端口号
int port=8888;
//buffer
ByteBuffer buffer=ByteBuffer.wrap("hello wuyimin".getBytes());
//绑定
ServerSocketChannel ssc=ServerSocketChannel.open();
ssc.bind(new InetSocketAddress(port));
//设置非阻塞模式
ssc.configureBlocking(false);
//一直监听有没有新的连接传入
while (true){
SocketChannel sc=ssc.accept();
//如果没有新的连接传入
if(sc==null){
System.out.println("无连接传入");
Thread.sleep(2000);
}else{
System.out.println("进来的连接:"+sc.socket().getRemoteSocketAddress());
buffer.rewind();//指针指向0,即位置0
sc.write(buffer);
sc.close();
}
}
}
输出效果:
无连接传入
无连接传入
进来的连接:/127.0.0.1:51728
进来的连接:/127.0.0.1:58135
无连接传入
进来的连接:/127.0.0.1:58544
SocketChannel
作用
- SocketChannel是用来连接Socket套接字的
- SocketChannel主要用于处理网路IO的通道
- SocketChannel基于TCP连接传输
- SocketChannel实现了可选择通道,可以被多路复用
特征:
- 对于已经存在的socket不可以创建socketChannel
- SocketChannel中提供open接口创建的Channel并没有进行网络级联,需要使用connect接口连接到指定的地址
- 未进行连接的SocketChannel执行IO操作时会抛出异常
- 支持阻塞IO和非阻塞IO
- 支持异步关闭
- 支持设定参数
public static void SocketChannelDemoTest() throws IOException {
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("www.baidu.com", 80));
//如果设置为阻塞模式,下面的语句就不会打印
socketChannel.configureBlocking(false);//设置非阻塞模式
//读操作
ByteBuffer buffer=ByteBuffer.allocate(16);
socketChannel.read(buffer);
socketChannel.close();
System.out.println("读完了");
}
DatagramChannel
之前介绍的两个channel面向的是TCP协议,而这个面向的是UDP协议
//发送
@Test
public void sendDatagram() throws IOException {
DatagramChannel sendChannel=DatagramChannel.open();
InetSocketAddress socketAddress=new InetSocketAddress("127.0.0.1",9999);
while (true){
ByteBuffer buffer = ByteBuffer.wrap("数据包...".getBytes("UTF-8"));
sendChannel.send(buffer,socketAddress);
System.out.println("已经完成了发送");
try{ TimeUnit.SECONDS.sleep(2); }catch (InterruptedException e){ e.printStackTrace(); }
}
}
//接受
@Test
public void receiveDatagram() throws IOException {
DatagramChannel receiveChannel=DatagramChannel.open();
InetSocketAddress receiveAddress=new InetSocketAddress(9999);
//绑定
receiveChannel.bind(receiveAddress);
//buffer
ByteBuffer buffer=ByteBuffer.allocate(1024);
//接受
while(true){
SocketAddress socketAddress=receiveChannel.receive(buffer);
System.out.println("发送地址"+socketAddress.toString());
buffer.flip();
System.out.println(Charset.forName("UTF-8").decode(buffer));
}
}
接收端输出:
发送地址/127.0.0.1:65050
数据包...
发送地址/127.0.0.1:65050
数据包...
发送地址/127.0.0.1:65050
数据包...
以上操作对应于:
//read和write
@Test
public void testConnection() throws IOException {
//打开DatagramChannel
DatagramChannel conChannel=DatagramChannel.open();
//绑定
conChannel.bind(new InetSocketAddress(9999));
//连接
conChannel.connect(new InetSocketAddress("127.0.0.1",9999));
//write
conChannel.write(ByteBuffer.wrap("数据包...".getBytes("UTF-8")));
//read
ByteBuffer buffer=ByteBuffer.allocate(1024);
while (true){
buffer.clear();
conChannel.read(buffer);
buffer.flip();
System.out.println(Charset.forName("UTF-8").decode(buffer));
}
}
Scatter和Gather(分散和聚集)
分散:从Channel中读取是指在读操作的时候将数据写入多个buffer中,因此Channel中读取的数据分散到多个Buffer中
聚集:写入Channel是指写操作的时候将多个buffer的数据写入聚集到同一个Channel
scatter/gather经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和头分散到不同的buffer中,这样可以方便处理头和体