NIO-Channel详解
每篇一句
但凡杀不死你的,终将使你变得更加强大
一、Channel简介
NIO中的所有IO都是从Channel开始的
- 读取数据:创建buffer缓冲区,然后数据会通过channel传到buffer,再从buffer中消费数据进行处理
- 写数据:创建buffer缓冲区,数据会先写到buffer中,再将buffer中的数据写到channel中
Channel和流区别
- 通道可读也可写,流都是单向的(使用流进行IO操作时需要分别创建一个输入流和一个输出流)
- 通道可以异步读写(因此可以结合Selector来实现一个线程管理多个channel通道)
- 通道总是基于缓冲区buffer来进行读写
Java NIO中最重要的Channel
- FileChannel:用于文件数据读写
- DatagramChannel:用于UDP的数据读写
- SocketChannel:用于TCP的数据读写(客户端)
- ServerSocketChannel:用于TCP的数据读写(服务端)
二、FileChannel的使用
使用FileChannel读取数据到Buffer(缓冲区)以及利用Buffer(缓冲区)写入数据到FileChannel
- 开启FileChannel
无法直接打开FileChannel(FileChannel是抽象类)。需要通过InputStream、OutputStream或RandomAccessFile获取FileChannel - 从FileChannel读取/写入数据
- 关闭FileChannel
public class FileChannel {
public static void main(String[] args) throws IOException {
//1.创建一个RandomAccessFile(随机访问文件)对象
RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\user.txt", "rw");
java.nio.channels.FileChannel fileChannel = randomAccessFile.getChannel();
//2.创建一个读数据缓冲区对象
ByteBuffer byteBufferRead = ByteBuffer.allocate(48);
//3.从通道中读取数据
int bytesRead = fileChannel.read(byteBufferRead);
//4.创建一个写数据缓冲区对象
ByteBuffer byteBufferWrite = ByteBuffer.allocate(48);
//5.写入数据
byteBufferWrite.put("FileChannel test".getBytes());
byteBufferWrite.flip();
fileChannel.write(byteBufferWrite);
while( bytesRead != -1) {
System.out.println("Read "+bytesRead);
//Buffer有两种模式,写模式和读模式。在写模式下调用flip()之后,Buffer从写模式变成读模式
byteBufferRead.flip();
// 读取buffer中的数据
while (byteBufferRead.hasRemaining()) {
System.out.print((char) byteBufferRead.get());
}
//清空缓冲区(切换到写模式)
byteBufferRead.clear();
bytesRead = fileChannel.read(byteBufferRead);
}
// 关闭RandomAccessFile对象
randomAccessFile.close();
}
}
三、SocketChannel和ServerSocketChannel的使用
服务器端
1.通过ServerSocketChannel 绑定ip地址和端口号
2.通过ServerSocketChannelImpl的accept()方法创建一个SocketChannel对象用户从客户端读/写数据
3.创建读数据/写数据缓冲区对象来读取客户端数据或向客户端发送数据
4. 关闭SocketChannel和ServerSocketChannel
public class Server {
public static void main(String[] args) {
try {
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannelOne = ServerSocketChannel.open();
serverSocketChannelOne.socket().bind(new InetSocketAddress("127.0.0.1",8080));
serverSocketChannelOne.configureBlocking(false);
//注册channel并指定监听的事件
serverSocketChannelOne.register(selector, SelectionKey.OP_ACCEPT);
ServerSocketChannel serverSocketChannelTwo = ServerSocketChannel.open();
serverSocketChannelTwo.socket().bind(new InetSocketAddress("127.0.0.1",8090));
serverSocketChannelTwo.configureBlocking(false);
//注册channel并指定监听的事件
serverSocketChannelTwo.register(selector, SelectionKey.OP_ACCEPT);
ByteBuffer readBuff = ByteBuffer.allocate(1024);
ByteBuffer writeBuff = ByteBuffer.allocate(1024);
writeBuff.put("received".getBytes());
writeBuff.flip();
while (true) {
int nReady = selector.select();
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
iterator.remove();
if (selectionKey.isConnectable()) {
System.out.println(Thread.currentThread().getId()+"start Connectable....");
} else if (selectionKey.isAcceptable()) {
System.out.println(Thread.currentThread().getId()+"start Acceptable....");
ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
}else if (selectionKey.isReadable()) {
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
readBuff.clear();
socketChannel.read(readBuff);
readBuff.flip();
System.out.println(Thread.currentThread().getId()+"-received:"+ new String(readBuff.array()));
selectionKey.interestOps(SelectionKey.OP_WRITE);
} else if (selectionKey.isWritable()) {
writeBuff.rewind();
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
socketChannel.write(writeBuff);
selectionKey.interestOps(SelectionKey.OP_READ);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端
1.通过SocketChannel连接到远程服务器
2.创建读数据/写数据缓冲区对象来读取服务端数据或向服务端发送数据
3.关闭SocketChannel
public class Client {
public static void main(String[] args) throws Exception{
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));
ByteBuffer writeBuffer = ByteBuffer.allocate(32);
ByteBuffer readBuffer = ByteBuffer.allocate(32);
writeBuffer.put("hello".getBytes());
writeBuffer.flip();
while (true) {
writeBuffer.rewind();
socketChannel.write(writeBuffer);
readBuffer.clear();
socketChannel.read(readBuffer);
}
}
}
四、DatagramChannel使用
服务器端
public class Server {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
DatagramChannel channel= DatagramChannel.open();
channel.configureBlocking(false);
channel.socket().bind(new InetSocketAddress(8080));
channel.register(selector, SelectionKey.OP_READ);
ByteBuffer byteBuffer = ByteBuffer.allocate(65535);
while (true) {
int n = selector.select();
if (n > 0) {
Iterator iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = (SelectionKey) iterator.next();
//必须手动删除
iterator.remove();
if (selectionKey.isReadable()) {
DatagramChannel datagramChannel = (DatagramChannel) selectionKey.channel();
byteBuffer.clear();
//读取数据
InetSocketAddress inetSocketAddress = (InetSocketAddress)datagramChannel
.receive(byteBuffer);
System.out.println(new java.lang.String(byteBuffer.array()));
//删除缓冲区的数据
byteBuffer.clear();
java.lang.String message = "data come from server";
byteBuffer.put(message.getBytes());
byteBuffer.flip();
//发送数据
datagramChannel.send(byteBuffer, inetSocketAddress);
}
}
}
}
}
}
客户端
public class Client {
public static void main(String[] args) throws IOException {
Selector selector = Selector.open();
DatagramChannel datagramChannel = DatagramChannel.open();
datagramChannel.configureBlocking(false);
SocketAddress socketAddress = new InetSocketAddress("localhost", 8080);
datagramChannel.connect(socketAddress);
datagramChannel.register(selector, SelectionKey.OP_READ);
datagramChannel.write(Charset.defaultCharset().encode("data come from client123!"));
ByteBuffer byteBuffer = ByteBuffer.allocate(100);
while (true) {
int n = selector.select();
if (n > 0) {
Iterator iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = (SelectionKey) iterator.next();
iterator.remove();
if (selectionKey.isReadable()) {
datagramChannel = (DatagramChannel) selectionKey.channel();
datagramChannel.read(byteBuffer);
System.out.println(new String(byteBuffer.array()));
byteBuffer.clear();
datagramChannel.write(Charset.defaultCharset()
.encode("data come from client456!"));
}
}
}
}
}
}

浙公网安备 33010602011771号