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!"));
					}
				}
			}
		}
	}
}
posted @ 2020-10-10 15:20  墨小雨的猫  阅读(370)  评论(0)    收藏  举报