1、通道channel是一种途径,用于以最小的开销来访问计算机(也是通道底层)的io服务。缓冲区则是对数据进行处理的终点
(1)、io分为文件io和流io,同样channel分为文件channel和socket channel.
(2)、FileChannel 文件通道
DatagramChannel 数据报通道,用于UDP协议。
SocketChannel 客户端通道
ServerSocketChannel 服务端通道
(3)、通道打开方式
(4)、与缓冲区不同,通道不能重复使用。一旦通道关闭,通道底层的io服务。调用通道的close()方法,可能导致通道底层的io线程阻塞,哪怕通道设置成飞阻塞状态。因此使用时可以调用isOpen()方法进行判断。
文件发散读取到多个Buffer中,多个buffer的文件聚集到同一channel中。channel无论读取或是写入都是从position这个位置开始的,其初始值为0.
file1.txt内容为:
hello wolrd
这是演示文本!!!
package testFileChannel;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class TestFileChannel {
public static void main(String[] args) throws Exception {
String fileName = "file1.txt";// 和src在同层目录
RandomAccessFile file = new RandomAccessFile(fileName, "rw");
testScatter(file);// channel内容发散到多个buffer输出
testGather(file);// 多个buffer的内容具体到同一个通道,读取通道并输出
}
public static void testGather(RandomAccessFile file) throws IOException {
FileChannel channel = file.getChannel();
ByteBuffer buffer1 = ByteBuffer.allocate(5);//初始容量为5个字节
ByteBuffer buffer2 = ByteBuffer.allocate(48);//初始容量为48个字节
channel.read(new ByteBuffer[]{buffer1,buffer2});//按顺序填充buffer
buffer1.flip();
buffer2.flip();//获取有效数据长度
while(buffer1.hasRemaining()) {
System.out.print((char)buffer1.get());//hello,刚好五个字节
}
System.out.println("\n");
byte[] b = buffer2.array();
System.out.println(new String(b));
// wolrd
//这是演示文本!!!
}
public static void testScatter(RandomAccessFile file) throws Exception {
FileChannel channel = file.getChannel();//获取通道
channel.position(file.length());//将channel的position调到文件内容的末尾位置,准备追加buffer的数据
ByteBuffer buffer1 = ByteBuffer.allocate(5);//初始容量为5个字节
ByteBuffer buffer2 = ByteBuffer.allocate(48);//初始容量为48个字节
buffer1.put("\nhehe".getBytes());//换行放入hehe四个字节
buffer2.put("\nnihao".getBytes());//换行放入nihao五个字节
buffer1.flip();
buffer2.flip();
while(buffer1.hasRemaining() || buffer2.hasRemaining()) {
channel.write(new ByteBuffer[]{buffer1,buffer2});
}
channel.force(true);//强制重新写入文件,file1.txt内容变为下面
//hello wolrd
//这是演示文本!!!
//hehe
//nihao
}
}
指通道写入数据时position数值很大,而position前面并没有内容,大多数是以0来占据。对磁盘空间消耗非常大,并且会降低我们的扫描速度,大大增加内存的消耗。
package testFileChannel;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class TestFileHole {
public static void main(String[] args) throws Exception {
File file = new File("file3.txt");
if(!file.exists()) {
file.createNewFile();
}
RandomAccessFile rf = new RandomAccessFile(file,"rw");
FileChannel channel = rf.getChannel();//获取通道后,然后插入数据。根据字节数来判断无用或占用磁盘空间的部分
ByteBuffer buffer = ByteBuffer.allocate(100);
putData(0,buffer,channel);
putData(5000000,buffer,channel);
putData(500,buffer,channel); //多次放入数据,但是最大position还是从5000000开始
System.out.println(channel.size());//5000018,前面不存在内容的部分就是文件空洞
}
public static void putData(int position, ByteBuffer buffer, FileChannel channel) throws Exception {
String str = "这是我们放入的数据";//gbk编码,此时每个占2个字节
buffer.clear();//每次调用让buffer处于写就绪状态
buffer.put(str.getBytes());//2*9=18字节
buffer.flip();
channel.position(position);
while(buffer.hasRemaining()) {
channel.write(buffer);
}
}
}
(1)、常见方法
public abstract class SocketChannel extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel
{ // This is a partial API listing
public static SocketChannel open( ) throws IOException;
public static SocketChannel open (InetSocketAddress remote) throws IOException;
public abstract Socket socket( );
public abstract boolean connect (SocketAddress remote) throws IOException;
public abstract boolean isConnectionPending( );
public abstract boolean finishConnect( ) throws IOException;
public abstract boolean isConnected( );
public final int validOps( );
}
(2)、客户端代码
package testSocket;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class ClientSocket {
public static void main(String[] args) {
SocketChannel client = null;
ByteBuffer buffer = ByteBuffer.allocate(100);
try {
client = SocketChannel.open();
client.connect(new InetSocketAddress("localhost",30));//绑定主机名和端口或者ip地址或者端口
client.configureBlocking(false);//非阻塞状态,多个客户端可以被一个服务端管理
if(client.isConnected()) { //连接成功时,doSomething
System.out.println("已连接到服务端");
buffer.put("您好!".getBytes()).flip();
while(buffer.hasRemaining()) {
client.write(buffer);
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
try {
client.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
}
服务端代码
package testSocket;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class ServerSocket {
public static void main(String[] args) throws Exception{
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(30));
SocketChannel accept = server.accept(); //获取客户端,读取客户端通道里面输入的内容
ByteBuffer buffer = ByteBuffer.allocate(100);
accept.read(buffer);
buffer.flip();
byte[] array = buffer.array();
System.out.println(new String(array));//您好!
}
}
无连接,connect只是指明发送数据包的地址。
正如SocketChannel模拟连接导向的流协议(如TCP/IP),DatagramChannel则模拟包导向的无连接协议(如UDP/IP)
DatagramChannel数据报的socket通道和其他socket通道创建一致,该通道以固定大小的数据包的形式send()发送数据或者receive()接受数据。Datagram既可作为服务端(监听者)也可以作为客户端(发送者)。
下面列出了一些选择数据报socket而非流socket的理由:
您的程序可以承受数据丢失或无序的数据。
您希望“发射后不管”(fire and forget)而不需要知道您发送的包是否已接收。
数据吞吐量比可靠性更重要。
您需要同时发送数据给多个接受者(多播或者广播)。
包隐喻比流隐喻更适合手边的任务。