Netty -入门IO与NIO

1、什么是Netty

       Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
       也就是说,Netty 是一个基于NIO的客户,服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。

2、什么是NIO

       Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。也有人将NIO 称之为 Non-block I/O (非阻塞I/O),貌似这个称呼更能体现NIO与传统IO的区别。

       Java NIO提供了与标准IO不同的IO工作方式:

       ① Channels and Buffers(通道和缓冲区):标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。
       ② Asynchronous IO(异步IO):Java NIO可以让你异步的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。
       ③Selectors(选择器):Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。

详细请参考《Java NIO 系列教程

 3、Netty给我们解决了什么问题

        功能强大,简化了NIO开发,Netty 是一个异步的,事件驱动的网络编程框架,使用它能够快速开发出高性能的、可维护性较强的、可扩展性较强的协议服务的服务端和客户端。

4、示例 标准IO

服务端

TimeServer:

package com.techstar.io;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Server
 * @author mrh
 *
 */
public class TimeServer {

	
	/**
	 * 启动服务
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		
		int port = 8081;
		ServerSocket server = null;
		try {
			server = new ServerSocket(port);
			System.out.println("TimeServer is started .......");
			Socket socket = null;
			while(true) {
				socket = server.accept();
				new Thread(new TimeServerHandler(socket)).start();
			}
		} finally {
			if (server != null) {
				System.out.println("The TimeServer is closed .......");
				server.close();
			}
		}
		
	}
}
 TimeServerHandler
package com.techstar.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date;

public class TimeServerHandler implements Runnable{

	private Socket socket;
	
	public TimeServerHandler(Socket socket) {
		super();
		this.socket = socket;
	}


	@Override
	public void run() {
		BufferedReader reader = null;
		PrintWriter writer = null;
		
		try {
			reader = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
			writer = new PrintWriter(this.socket.getOutputStream(), true);
			
			String currentTime = null;
			String body = null;
			
			while(true) {
				body = reader.readLine();
				if (body == null) 
					break;
				System.out.println("The time server receive order:" + body);
				currentTime = "QUERY TIME ORDER".equals(body)?new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
				writer.println(currentTime);
				writer.flush();
			}
		} catch (Exception e) {
			if (writer != null) {
				writer.close();
				writer = null;
			}
			
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
			if (this.socket != null) {
				try {
					this.socket.close();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				this.socket = null;
			}
		}
		
	}

}
 

客户端:

package com.techstar.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class TimeClient {

	
	public static void main(String[] args) {
		int port = 8081;
		BufferedReader reader = null;
		PrintWriter writer = null;
		Socket socket = null;
		try {
			socket = new Socket("127.0.0.1", port);
			reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			writer = new PrintWriter(socket.getOutputStream(), true);
			
			writer.println("QUERY TIME ORDER");
			writer.flush();
			System.out.println("send order to server successed!");
			String response = reader.readLine();
			System.out.println("Now is " + response);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (writer != null) {
				writer.close();
				writer = null;
			}
			
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
			if (socket != null) {
				try {
					socket.close();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				socket = null;
			}
		}
		
	}
}

 5、 Linux网络 I/O 模型简介

① 阻塞 I/O 模型

 最常用的 I/O 模型就是阻塞 I/O 模型, 缺省情形下, 所有文件操作都是阻塞的。如下图所示

 

优劣:简单,效率低

② 非阻塞 I/O 模型

理解了阻塞I/O,非阻塞I/O就好理解。非阻塞I/O是程序执行过程中,I/O操作不会阻塞程序的执行,也就是在I/O操作的同时,继续执行其他代码(这得益于Node的事件循环机制)。在I/O设备效率还远远低于CPU效率的时代,这种I/O模型(非阻塞I/O)为程序带来的性能上的提高是非常可观的

非阻塞IO模型

 优劣:效率高,复杂

③ I/O 复用模型

 让应用程序可以同时对多个I/O端口进行监控以判断其上的操作是否可以进行,达到时间复用的目的。在书上看到一个例子来解释I/O的原理,我觉得很形象,如果用监控来自10根不同地方的水管(I/O端口)是否有水流到达(即是否可读),那么需要10个人(即10个线程或10处代码)来做这件事。如果利用某种技术(比如摄像头)把这10根水管的状态情况统一传达到某一点,那么就只需要1个人在那个点进行监控就行了,而类似与select或epoll这样的多路I/O复用机制就好比是摄像头的功能,它们能够把多个I/O端口的状况反馈到同一处,比如某个特定的文件描述符上,这样应用程序只需利用对应的select()或epoll_wait()系统调用阻塞关注这一处即可。
优劣:由于I/O多路复用是在单一进程的上下文中的,因此每个逻辑流程都能访问该进程的全部地址空间,所以开销比多进程低得多;缺点是编程复杂度高

④ 信号驱动 I/O 模型

 让内核在描述字就绪时发送SIGIO信号通知我们。首先开启套接口的信号驱动1/O功能,sigaction系统调用安装一个信号处理函数,当内核数据包准备好时,会为该进程产生一个SIGIO信号。

⑤ 异步 I/O告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核复制到用户自己的缓冲区)通知我们。与信号模型的区别就在于:信号驱动IO由内核通知我们何时可以开始下一个IO操作;异步I/O模型由内核通知我们I/O操作何时完成。

6、NIO的主要组件

① Buffer

缓冲区, 几乎每种Java数据类型都有一个

② Channel

数据就是一个通道, 网络数据通过Channel读取写入。与传统的IO不同, 它是双向的,允许同时进行读写操作。

③ Selector

NIO的基础,掌握Selector的熟练度,就决定了对NIO编程的熟练度。

Selector(选择器)能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。

好处:

仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通道

  

猜你喜欢

转载自muruiheng.iteye.com/blog/2320619