初识Netty之对BIO和NIO的认识

Netty 是由 JBOSS 提供的基于NIO一个 Java 开源网络编程框架。Netty 提供异步的、基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠性的网络 IO 程序。作为当前最流行的 NIO 框架,Netty应用很广泛, hadoop、dubbo等底层RPC都是基于Netty实现的。以下是实现的章节:

Netty 是基于NIO的网络编程框架,那么了解Netty之前,先来熟悉下BIO和NIO。

  • 阻塞式IO-BIO

BIO 是block(阻塞) IO的简称,由于主要涉及网络编程部分,所有主要介绍下BIO中的网络IO,也就是Socket编程。在JDK1.4之前的版本,建立网络连接只能采用BIO,其实目前也有很多系统依然采用这种的方式建立网络关系,目前公司的系统 也是采用这种方式。通过BIO建立网络连接,需要现在服务端启用一个ServerSocket来接收接收处理返回信息,在客户启用一个Socket来对服务端进行通信。在默认的情况下,客户端发送请求会先咨询服务端是否有线程响应,如果没有则会一直等待或者直接遭到拒绝,这就是阻塞IO。

下面实现一个简单Socket通信:

/**
 * 服务器端程序
 * @author Administrator
 */
public class Server {
	public static void main(String[] args) throws Exception {
		//1.创建 ServerSocket 对象
		ServerSocket ss=new ServerSocket(8888);
		while (true) {
			//2.监听客户端
			Socket s = ss.accept(); //阻塞
			//3.从连接中取出输入流来接收消息
			InputStream is = s.getInputStream(); //阻塞
			byte[] b = new byte[10];
			is.read(b);
			String clientIP = s.getInetAddress().getHostAddress();
			System.out.println(clientIP + "说:" + new String(b).trim());
			//4.从连接中取出输出流并回话
			OutputStream os = s.getOutputStream();
			os.write("太高了".getBytes());
			os.close();
		}
	}
}
/**
 * 客户端程序
 * @author Administrator
 */
public class Client {
	public static void main(String[] args) throws Exception {
		while (true) {
			//1.创建 Socket 对象
			Socket s = new Socket("127.0.0.1", 8888);
			//2.从连接中取出输出流并发消息
			OutputStream os = s.getOutputStream();
			System.out.println("请输入房租:");
			Scanner sc = new Scanner(System.in);
			String msg = sc.nextLine();
			os.write(msg.getBytes());
			//3.从连接中取出输入流并接收回话
			InputStream is = s.getInputStream(); //阻塞
			byte[] b = new byte[20];
			is.read(b);
			System.out.println("小明说:" + new String(b).trim());
			//4.关闭资源
			s.close();
		}
	}
}

在上面的例子运行过程中可以看出,如果服务端没有返回数据,那么客户端会一直等待,程序也就会阻塞在这里,直到返回或者失败。

  • 那什么又是不阻塞IO呢?

从JDK1.4开始,Java提供了一种的新的网络编程模式也就是NIO,全称是 java non-blocking IO,也就是非阻塞式IO,由于改进了一系列的输入/输出的新特性所以也称之为NEW IO。由于NIO的网络通道是非阻塞的NIO,基于事件驱动,非常适用于服务器需要为维持大量连接,但是数据量不大的情况,比如及时通讯等场景。NIO和BIO有着相同的目的和作用,但是实现方式却大不相同:

1、数据的处理方式:BIO是以流的形式处理数据,NIO是以块的形式的处理数据,块IO的处理效率要不比流IO高很多。

2、BIO是阻塞式的,NIO是非阻塞式的,NIO可以提供非阻塞式的高伸缩性网络。

NIO的具体操作(网络IO):

NIO有三大核心包括:Channel(通道),Buffer(缓冲区), Selector(选择器)。NIO 基于 Channel(通道)和 Buffer(缓冲区)进行操作,数据。总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接请求,数据到达等),因此使用单个线程就可以监听多个客户端通道。

 Selector(选择器):能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的处理。这样就可以只用一个单线程去管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间的上下文切换导致的开销。

Channel:网络 IO 通道,具体负责进行读写操作。NIO 总是把缓冲区的数据写入通道,或者把通道里的数据读到缓冲区。

Buffer(缓冲区):缓冲区就像一个数组,可以保存多个相同类型的数据。

通过NIO实现Socket通讯的三种方式:

1、一个客户端连接用一个线程,优点:程序编写简单;缺点:如果连接非常多,分配的线程也会非常多,服务器可能会因为资源耗尽而崩溃。
 2、把每一个客户端连接交给一个拥有固定数量线程的连接池,优点:程序编写相对简单,可以处理大量的连接。确定:线程的开销非常大,连接如果非常多,排队现象会比较严重。

3、使用 Java 的 NIO,用非阻塞的 IO 方式处理。这种模式可以用一个线程,处理大量的客
户端连接。

代码实现一个NIO示例:

/**
 * 网络服务器端程序
 * @author Administrator
 */
public class Server {
	public static void main(String[] args) throws Exception{
		//1. 获取一个 ServerSocketChannel 通道
		ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
		//2. 得到一个 与之关联的Selector 对象 
		Selector selector=Selector.open();
		//3. 绑定端口号8090
		serverSocketChannel.bind(new InetSocketAddress(8090));
		//4. 设置非阻塞方式
		serverSocketChannel.configureBlocking(false);
		//5. 把 ServerSocketChannel 对象注册给 Selector 对象
		//SelectionKey-通道与选择器的注册关系(OP_ACCEP-有新的网络连接可以 accept,值为 16)
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		//6. 开会干活
		while(true){
			//6.1 监控客户端
			if(selector.select(2000)==0){ //nio 非阻塞式的优势
				System.out.println("Server:没有客户请求,去做别的事");
				continue;
			}
			//6.2 得到 SelectionKey,判断通道里的事件
			Iterator<SelectionKey> keyIterator=selector.selectedKeys().iterator();
			while(keyIterator.hasNext()){
				SelectionKey key=keyIterator.next();
				if(key.isAcceptable()){ //客户端连接请求事件
					System.out.println("OP_ACCEPT");
					//接受一个连接,返回代表这个连接的通道对象
					SocketChannel socketChannel=serverSocketChannel.accept();
					//接受一个连接,返回代表这个连接的通道对象
					socketChannel.configureBlocking(false);
					//注册一个选择器并设置监听事件
					//SelectionKey.OP_READ代表了读操作
					//分配一个1M的字节缓冲区
					socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
				}
				if(key.isReadable()){ //读取客户端数据事件
					SocketChannel channel=(SocketChannel) key.channel();
					ByteBuffer buffer=(ByteBuffer) key.attachment();
					channel.read(buffer);
					System.out.println("客户端发来数据:"+new String(buffer.array()));
				}
				//6.3 手动从集合中移除当前 key,防止重复处理
				keyIterator.remove();
			}
		}
	}
}
/**
 * 网络客户端程序
 * @author Administrator
 */
public class Client {
	public static void main(String[] args) throws Exception{
		//1. 得到一个网络通道
		SocketChannel channel=SocketChannel.open();
		//2. 设置非阻塞方式
		channel.configureBlocking(false);
		//3. 提供服务器端的 IP 地址和端口号
		InetSocketAddress address=new InetSocketAddress("127.0.0.1",8090);
		//4. 连接服务器端
		if(!channel.connect(address)){
			while(!channel.finishConnect()){ //nio 作为非阻塞式的优势
				System.out.println("Client:连接服务器端的同时,可以去干别的事情");
			}
		}
		//5. 得到一个缓冲区并存入数据
		String msg="hello,Server";
		//将 byte 数组包装到缓冲区中
		ByteBuffer writeBuf = ByteBuffer.wrap(msg.getBytes());
		//6. 发送数据
		channel.write(writeBuf);
		System.in.read();
	}
}

NIO和BIO了解完了,下面通过表格的方式来对比下这两种网络IO的区别和共同点:

对比总结BIONIO
IO方式同步阻塞同步非阻塞(多路复用)
可靠性

吞吐量


对BIO和NIO有了一个比较全面的认识之后,下一篇就正式开始进入Netty框架学习。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值