`
yangyangmyself
  • 浏览: 232419 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

JAVA NIO 之一

    博客分类:
  • Java
阅读更多
传统IO 写道

  网络传输方式问题:传统的RPC框架或者基于RMI等方式的远程服务(过程)调用采用了同步阻塞IO,当客户端的并发压力或者网络时延增大之后,同步阻塞IO会由于频繁的wait导致IO线程经常性的阻塞,由于线程无法高效的工作,IO处理能力自然下降。下面,我们通过BIO通信模型图看下BIO通信的弊端:

  采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,接收到客户端连接之后为客户端连接创建一个新的线程处理请求消息,处理完成之后,返回应答消息给客户端,线程销毁,这就是典型的一请求一应答模型。该架构最大的问题就是不具备弹性伸缩能力,当并发访问量增加后,服务端的线程个数和并发访问数成线性正比,由于线程是JAVA虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能急剧下降,随着并发量的继续增加,可能会发生句柄溢出、线程堆栈溢出等问题,并导致服务器最终宕机。

NIO 写道

  在IO编程过程中,当需要同时处理多个客户端接入请求时,可以利用多线程或者IO多路复用技术进行处理。IO多路复用技术通过把多个IO的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。与传统的多线程/多进程模型比,I/O多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行,降低了系统的维护工作量,节省了系统资源。
  JDK1.4提供了对非阻塞IO(NIO)的支持,JDK1.5_update10版本使用epoll替代了传统的select/poll,极大的提升了NIO通信的性能。
  JAVA NIO实现服务端与客户端简单数据传输

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
/**
* 服务端
*/
public class SocketServer {

	/**
	 * 服务器默认绑定端口
	 */
	public static final int DEFAULT_PORT = 9999;

	/**
	 * 选择器
	 */
	public Selector selector;

	public SocketServer(String ip, int port) {
		ServerSocketChannel ssc = null;
		try {
			int _port = DEFAULT_PORT;
			if (port > 0)
				_port = port;
			/* 获取通道 */
			ssc = ServerSocketChannel.open();
			/* 配置非阻塞 */
			ssc.configureBlocking(false);
			/**
			 * 配置绑定端口 ServerSocketChannel没有bind()方法,
			 * 因此有必要取出对等的ServerSocket并使用它来绑定到一
			 * 个端口以开始监听连接
			 */
			ssc.socket().bind(new InetSocketAddress(ip, _port));
			/* 获取选择器 */
			this.selector = Selector.open();
			/* 将通道注册到选择器 */
			ssc.register(this.selector, SelectionKey.OP_ACCEPT);
		}catch(ClosedChannelException e1){
			System.out.println("关闭的通道,无法注册到选择器");
			e1.printStackTrace();
		} catch (IOException e2) {
			try {
				if(ssc != null) ssc.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			System.out.println("服务器绑定端口冲突");
			e2.printStackTrace();
		}
	}
	
	/**
	 * 轮询选择器
	 * @throws Exception
	 */
	public void pollSelect() throws Exception {
		/* (阻塞)轮询选择器,直到有事件 */
		while (this.selector.select()>0) {
			/* 获取事件通知列表 */
			Iterator<SelectionKey> it = this.selector.selectedKeys().iterator();
			while (it.hasNext()) {
				SelectionKey selectKey = it.next();
				it.remove();
				try {
					process(selectKey);
				} catch (Exception e) {
					e.printStackTrace();
					continue;
				}
			}
		}
	}

	/**
	 * 事件处理
	 * @param selectKey
	 */
	public void process(SelectionKey selectKey) throws Exception{
		if (selectKey.isAcceptable()) { /* 客户端连接事件 */
			accept(selectKey);
		} else if (selectKey.isReadable()) { /* 可读事件 */
			read(selectKey);
		}
	}
	
	/**
	 * 连接事件
	 * @param selectKey
	 */
	public void accept(SelectionKey selectKey) throws Exception {
		ServerSocketChannel ssc = null;
		try {
			ssc = (ServerSocketChannel) selectKey
					.channel();
			SocketChannel sc = ssc.accept();
			sc.configureBlocking(false);
			/* 发送信息 */
			sc.write(ByteBuffer.wrap(new String("Hello World!")
					.getBytes()));
			/* 注册读事件 */
			sc.register(this.selector, SelectionKey.OP_READ);
		} catch (ClosedChannelException e) {
			if(ssc!=null) 
				ssc.close();
			throw new IOException("关闭的通道,无法注册到选择器");
		} catch (IOException e) {
			if(ssc!=null) 
				ssc.close();
			throw new IOException("连接服务或配置失败!");
		}
	}
	
	/**
	 * 可读事件
	 * @param selectKey
	 */
	public void read(SelectionKey selectKey) throws Exception{
		SocketChannel channel = null;
		try {
			// 服务器可读取消息:得到事件发生的Socket通道
			channel = (SocketChannel) selectKey.channel();
			// 创建读取的缓冲区
			ByteBuffer buffer = ByteBuffer.allocate(100);
			channel.read(buffer);
			byte[] data = buffer.array();
			String msg = new String(data).trim();
			System.out.println("客户端:" + msg);
			// ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
			// 将消息回送给客户端
			// channel.write(outBuffer);
		} catch (Exception e) {
			if(channel != null) 
				channel.close();
			throw new Exception("客户端将通道关闭,无法从通道读入缓冲或将缓冲数据写回通道!");
		}
	}
	
	
	public static void main(String[] args) {
		SocketServer ss = null;
		try {
			ss = new SocketServer("localhost", 9999);
			ss.pollSelect();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}




import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
/**
* 客户端
*/
public class SocketClient {

	public Selector selector;
	
	public SocketClient(String ip, int port){
		SocketChannel channel = null;
		try {
			//channel = SocketChannel.open(new InetSocketAddress(ip,port));
			channel = SocketChannel.open();
			// 设置通道为非阻塞  
	        channel.configureBlocking(false);
	        // 获得一个通道管理器  
	        this.selector = Selector.open();
	        // 客户端连接服务器,其实方法执行并没有实现连接 
	        channel.connect(new InetSocketAddress(ip, port)); 
	        /**while(!channel.finishConnect()){
	        	System.out.println("尝试连接....");
	        }*/
	        // 注册连接事件。  
	        channel.register(this.selector, SelectionKey.OP_CONNECT);  
		} catch(ClosedChannelException e1){
			System.out.println("关闭的通道,无法注册到选择器");
			e1.printStackTrace();
		} catch (IOException e2) {
			System.out.println("连接异常!");
			try {
				if(channel != null) channel.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			e2.printStackTrace();
		}
	}
	
	/**
	 * 轮询选择器
	 * @throws IOException
	 */
	public void  pollSelect() throws Exception {  
		/* (阻塞)轮询选择器,直到有事件 */
		while ( this.selector.select() > 0 ) {
            /* 获取事件通知列表 */
            Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator();  
            while (ite.hasNext()) {  
                SelectionKey selectKey = (SelectionKey) ite.next();  
                // 删除已选的key,以防重复处理  
                ite.remove();  
                process(selectKey);
            }  
        }  
    }
	
	/**
	 * 处理事件
	 * @param selectKey
	 */
	public void process(SelectionKey selectKey) throws Exception{
        if (selectKey.isConnectable()) {
        	connect(selectKey);
        } else if (selectKey.isReadable()) {  
        	read(selectKey);
        }  
	}
	
	/**
	 * 连接事件
	 * @param selectKey
	 * @throws Exception
	 */
	public void connect(SelectionKey selectKey) throws Exception{
		try {
			SocketChannel channel = (SocketChannel) selectKey  
			        .channel();  
			/* 如果正在连接,则完成连接 */  
			if(channel.isConnectionPending()){
				/**
				 * connect()方法尚未被调用,调用finishConnect()方法,
				 * 那么将产生NoConnectionPendingException
				 */
			    channel.finishConnect();  
			}
			/**
			 * 在非阻塞模式下调用connect()方法之后,SocketChannel又被切换回了阻塞模式;那么如果
			 * 有必要的话,调用线程会阻塞直到连接建立完成,finishConnect()方法接着就会返回true
			 * 值。
			 */
			/* 设置成非阻塞 */  
			channel.configureBlocking(false);  
			/* 给服务端发送信息 */
			channel.write(ByteBuffer.wrap(new String("编号001客户端连接成功!").getBytes()));  
			/* 注册读事件 */  
			channel.register(this.selector, SelectionKey.OP_READ);
		} catch (ClosedChannelException e) {
			throw new IOException("关闭的通道,无法注册到选择器");
		} catch (IOException e) {
			throw new IOException("连接服务或配置失败!");
		}
	}
	
	/**
	 * 读事件
	 * @param selectKey
	 * @throws Exception
	 */
	public void read(SelectionKey selectKey) throws Exception{
		try {
			// 服务器可读通道
			SocketChannel channel = (SocketChannel) selectKey.channel(); 
			// 创建读取的缓冲区  
			ByteBuffer buffer = ByteBuffer.allocate(100);  
			channel.read(buffer);  
			byte[] data = buffer.array();  
			String msg = new String(data).trim();  
			System.out.println(msg);  
			ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
			// 将消息回送给服务端  
			//channel.write(outBuffer);
		} catch (Exception e) {
			throw new IOException("服务端将通道关闭,无法从通道读入缓冲或将缓冲数据写回通道!");
		}
	}
	
	public static void main(String[] args) {
		SocketClient sc = null;
		try {
			sc = new SocketClient("localhost", 9999);
			sc. pollSelect();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

  • 大小: 21.4 KB
0
1
分享到:
评论
1 楼 successfultzb 2016-01-15  
  
刚好准备了解这个,讲的很详细明了,赞

相关推荐

    java NIO.zip

    通道是NIO中的核心概念之一,它提供了从一个数据源(如文件、套接字)到另一个数据源的数据传输路径。Java NIO支持多种类型的通道,包括文件通道(FileChannel)、套接字通道(SocketChannel)和服务器套接字通道...

    java nio 实现socket

    **NIO非阻塞模式**:相比之下,NIO采用了非阻塞模式,即当没有数据可读时,`read()`方法不会阻塞,而是立即返回。这意味着应用程序可以同时处理多个输入/输出操作,而不需要为每个操作分配一个独立的线程。这样的...

    JAVA NIO学习网站

    Java NIO(New IO)是Java 1.4版本引入的一个新API,全称为New Input/Output,是对传统IO(Old IO)的扩展。...对于从事Java后端开发或者网络编程的工程师来说,深入理解并熟练运用Java NIO是必备的技能之一。

    JAVA NIO 异步通信客户端

    1. **Channels**: Channel是NIO的核心组件之一,它提供了一种从一个数据源(如文件、套接字或缓冲区)到另一个数据源传输数据的方式。SocketChannel是用于网络通信的通道,可以用于客户端与服务器建立TCP连接。 2. ...

    Java语言基础教程-Java NIO流篇2

    首先,我们要理解Java NIO的核心组件之一——流。在Java的IO体系中,流是数据传输的抽象,它代表了数据的流向,可以是输入流(InputStream)或输出流(OutputStream)。然而,NIO中的流与传统的IO流有所不同,它们...

    《Java NIO》Download Url

    它们提供了对缓冲区(Buffers)的读写操作,是NIO的核心组件之一。 2. **缓冲区(Buffers)**:在NIO中,所有I/O操作都是通过缓冲区进行的。缓冲区是一个可以容纳特定类型数据的容器,比如ByteBuffer、CharBuffer、...

    高手使用Java NIO编写高性能的服务器

    Java NIO(New IO)是Java 1.4版本引入的一个新特性,它为Java程序提供了非阻塞I/O操作的能力,极大地提升了Java在处理网络通信和文件读写时的性能。NIO与传统的IO( Blocking IO)模型相比,最大的区别在于其非阻塞...

    Java NIO与IO性能对比分析.pdf

    Java NIO(New IO,也称为Non-blocking IO)和传统的Java IO是Java编程语言中用于处理I/O操作的两种主要技术。随着互联网用户数量的激增,企业对应用程序的并发处理能力提出了更高的要求。为了解决传统Java IO模型在...

    javaNIO很好的资料

    缓冲区是 NIO 的核心组件之一,用于存储数据。Java NIO 提供了多种缓冲区,每种缓冲区对应不同的数据类型: - **ByteBuffer**:用于存储 byte 类型的数据。 - **CharBuffer**:用于存储 char 类型的数据。 - **...

    Java视频教程 Java游戏服务器端开发 Netty NIO AIO Mina视频教程

    [第4节] JavaNIO流-通道1.flv [第5节] Java NIO流-通道2.flv [第6节] Java NIO流-socket通道操作.flv [第7节] Java NIO流-文件通道操作.flv [第8节] Java NIO流-选择器 .flv [第9节] Java NIO流-选择器操作.flv...

    Java NIO - The NIO Architecture

    随着J2SE 1.4的发布,Java引入了一个全新的I/O架构——NIO(New I/O),旨在解决传统I/O模型中的一些问题并提供更高效的处理方式。本篇文章将深入探讨NIO的核心概念、内部结构以及它如何改变了我们处理文件和网络...

    Java NIO非阻塞服务端与客户端相互通信

    - **缓冲区(Buffers)**:NIO中的数据操作都在缓冲区上进行,这是NIO的主要特性之一,缓冲区提供了更高效的数据存取方式。 - **选择器(Selectors)**:选择器允许单个线程监视多个通道,当通道准备好进行读写...

Global site tag (gtag.js) - Google Analytics