一、NIO类库简介

  1、缓冲区Buffer

  Buffer是一个对象,包含一些要写入和读出的数据。

  在NIO中,所有的数据都是用缓冲区处理的,读取数据时,它是从通道(Channel)直接读到缓冲区中,在写入数据时,也是从缓冲区写入到通道。

  缓冲区实质上是一个数组,通常是一个字节数组(ByteBuffer),也可以是其它类型的数组,此外缓冲区还提供了对数据的结构化访问以及维护读写位置等信息。

  Buffer类的继承关系如下图所示:

  

  2、通道Channel

  Channel是一个通道,网络数据通过Channel读取和写入。通道和流的不同之处在于通道是双向的(通道可以用于读、写后者二者同时进行),流只是在一个方向上移动。

  Channel大体上可以分为两类:用于网络读写的SelectableChannel(ServerSocketChannel和SocketChannel就是其子类)、用于文件操作的FileChannel。

  下面的例子给出通过FileChannel来向文件中写入数据、从文件中读取数据,将文件数据拷贝到另一个文件中:

public class NioTest
{
public static void main(String[] args) throws IOException
{
copyFile();
}
//拷贝文件
private static void copyFile()
{
FileInputStream in=null;
FileOutputStream out=null;
try
{
in=new FileInputStream("src/main/java/data/in-data.txt");
out=new FileOutputStream("src/main/java/data/out-data.txt");
FileChannel inChannel=in.getChannel();
FileChannel outChannel=out.getChannel();
ByteBuffer buffer=ByteBuffer.allocate(1024);
int bytesRead = inChannel.read(buffer);
while (bytesRead!=-1)
{
buffer.flip();
outChannel.write(buffer);
buffer.clear();
bytesRead = inChannel.read(buffer);
}
}
catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//写文件
private static void writeFileNio()
{
try
{
RandomAccessFile fout = new RandomAccessFile("src/main/java/data/nio-data.txt", "rw");
FileChannel fc=fout.getChannel();
ByteBuffer buffer=ByteBuffer.allocate(1024);
buffer.put("hi123".getBytes());
buffer.flip();
try
{
fc.write(buffer);
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//读文件
private static void readFileNio()
{
FileInputStream fileInputStream;
try
{
fileInputStream = new FileInputStream("src/main/java/data/nio-data.txt");
FileChannel fileChannel=fileInputStream.getChannel();//从 FileInputStream 获取通道
ByteBuffer byteBuffer=ByteBuffer.allocate(1024);//创建缓冲区
int bytesRead=fileChannel.read(byteBuffer);//将数据读到缓冲区
while(bytesRead!=-1)
{
/*limit=position
* position=0;
*/
byteBuffer.flip();
//hasRemaining():告知在当前位置和限制之间是否有元素
while (byteBuffer.hasRemaining())
{
System.out.print((char) byteBuffer.get());
}
/*
* 清空缓冲区
* position=0;
* limit=capacity;
*/
byteBuffer.clear();
bytesRead = fileChannel.read(byteBuffer);
}
} catch (FileNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

  3、多路复用器Selector

  多路复用器提供选择已经就绪的任务的能力。Selector会不断的轮询注册在其上的Channel,如果某个Channel上面发送读或者写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合,进行后续的I/O操作。

  一个多路复用器Selector可以同时轮询多个Channel,由于JDK使用了epoll代替了传统的select实现,所以它没有最大连接句柄1024/2048的限制,意味着只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端。其模型如下图所示:

  

  用单线程处理一个Selector。要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。

  注:

  1、什么select模型?

  select是事件触发机制,当等待的事件发生就触发进行处理,多用于Linux实现的服务器对客户端的处理。

  可以阻塞地同时探测一组支持非阻塞的IO设备,是否有事件发生(如可读、可写,有高优先级错误输出等),直至某一个设备触发了事件或者超过了指定的等待时间。也就是它们的职责不是做IO,而是帮助调用者寻找当前就绪的设备。

  2、什么是epoll模型?

  epoll的设计思路,是把select/poll单个的操作拆分为1个epoll_create+多个epoll_ctrl+一个wait。此外,内核针对epoll操作添加了一个文件系统”eventpollfs”,每一个或者多个要监视的文件描述符都有一个对应的eventpollfs文件系统的inode节点,主要信息保存在eventpoll结构体中。而被监视的文件的重要信息则保存在epitem结构体中。所以他们是一对多的关系。

二、NIO服务器端开发

  功能说明:开启服务器端,对每一个接入的客户端都向其发送hello字符串。

  使用NIO进行服务器端开发主要有以下几个步骤:

  1、创建ServerSocketChannel,配置它为非阻塞模式

    serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);

  2、绑定监听,配置TCP参数,如backlog大小

    serverSocketChannel.socket().bind(new InetSocketAddress(8080));

  3、创建一个独立的I/O线程,用于轮询多路复用器Selector

  4、创建Selector,将之前创建的ServerSocketChannel注册到Selector上,监听SelectionKey.ACCEPT

  selector=Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

  5、启动I/O线程,在循环体内执行Selector.select()方法,轮询就绪的Channel

  while(true)
{
try
{
//select()阻塞到至少有一个通道在你注册的事件上就绪了
//如果没有准备好的channel,就在这一直阻塞
//select(long timeout)和select()一样,除了最长会阻塞timeout毫秒(参数)。
selector.select();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
break;
}
 }

  6、当轮询到了处于就绪状态的Channel时,需对其进行判断,如果是OP_ACCEPT状态,说明是新的客户端接入,则调用ServerSocketChannel.accept()方法接受新的客户端

      //返回已经就绪的SelectionKey,然后迭代执行
Set<SelectionKey> readKeys=selector.selectedKeys();
for(Iterator<SelectionKey> it=readKeys.iterator();it.hasNext();)
{
SelectionKey key=it.next();
it.remove();
try
{
if(key.isAcceptable())
{
ServerSocketChannel server=(ServerSocketChannel) key.channel();
SocketChannel client=server.accept();
client.configureBlocking(false);
client.register(selector,SelectionKey.OP_WRITE);
}
else if(key.isWritable())
{
SocketChannel client=(SocketChannel) key.channel();
ByteBuffer buffer=ByteBuffer.allocate(20);
String str="hello";
buffer=ByteBuffer.wrap(str.getBytes());
client.write(buffer);
key.cancel();
}
}catch(IOException e)
{
e.printStackTrace();
key.cancel();
try
{
key.channel().close();
} catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
} }
}

  7、设置新接入的客户端链路SocketChannel为非阻塞模式,配置其他的一些TCP参数

  if(key.isAcceptable())
{
ServerSocketChannel server=(ServerSocketChannel) key.channel();
SocketChannel client=server.accept();
client.configureBlocking(false);
...
}

  8、将SocketChannel注册到Selector,监听OP_WRITE

  client.register(selector,SelectionKey.OP_WRITE);

  9、如果轮询的Channel为OP_WRITE,则说明要向SockChannel中写入数据,则构造ByteBuffer对象,写入数据包

  else if(key.isWritable())
{
SocketChannel client=(SocketChannel) key.channel();
ByteBuffer buffer=ByteBuffer.allocate(20);
String str="hello";
buffer=ByteBuffer.wrap(str.getBytes());
client.write(buffer);
key.cancel();
}

  完整代码如下:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set; public class ServerSocketChannelDemo
{
public static void main(String[] args)
{
ServerSocketChannel serverSocketChannel;
Selector selector=null;
try
{
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(8080));
selector=Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
while(true)
{
try
{
//select()阻塞到至少有一个通道在你注册的事件上就绪了
//如果没有准备好的channel,就在这一直阻塞
//select(long timeout)和select()一样,除了最长会阻塞timeout毫秒(参数)。
selector.select();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
break;
}
//返回已经就绪的SelectionKey,然后迭代执行
Set<SelectionKey> readKeys=selector.selectedKeys();
for(Iterator<SelectionKey> it=readKeys.iterator();it.hasNext();)
{
SelectionKey key=it.next();
it.remove();
try
{
if(key.isAcceptable())
{
ServerSocketChannel server=(ServerSocketChannel) key.channel();
SocketChannel client=server.accept();
client.configureBlocking(false);
client.register(selector,SelectionKey.OP_WRITE);
}
else if(key.isWritable())
{
SocketChannel client=(SocketChannel) key.channel();
ByteBuffer buffer=ByteBuffer.allocate(20);
String str="hello";
buffer=ByteBuffer.wrap(str.getBytes());
client.write(buffer);
key.cancel();
}
}catch(IOException e)
{
e.printStackTrace();
key.cancel();
try
{
key.channel().close();
} catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
} }
}
}
}
}

  我们用telnet localhost 8080模拟出多个客户端:

  

  程序运行结果如下:

  

三、参考资料

  1、netty权威指南(李林峰)

  

Java NIO服务器端开发的更多相关文章

  1. Netty精粹之JAVA NIO开发需要知道的

    学习Netty框架以及相关源码也有一小段时间了,恰逢今天除夕,写篇文章总结一下.Netty是个高效的JAVA NIO框架,总体框架基于异步非阻塞的设计,基于网络IO事件驱动,主要贡献在于可以让用户基于 ...

  2. Eclipse下Android开发错误之Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace

    升级了Android版本后,在运行应用时提示: [2013-11-27 10:37:35 - Dex Loader] Unable to execute dex: java.nio.BufferOve ...

  3. Java NIO开发需要注意的陷阱(转)

    陷阱1:处理事件忘记移除key在select返回值大于0的情况下,循环处理Selector.selectedKeys集合,每处理一个必须从Set中移除 Iterator<SelectionKey ...

  4. 我的Java开发学习之旅------>Java NIO 报java.nio.charset.MalformedInputException: Input length = 1异常

    今天在使用Java NIO的Channel和Buffer进行文件操作时候,报了java.nio.charset.MalformedInputException: Input length = 1异常, ...

  5. JAVA NIO异步通信框架MINA选型和使用的几个细节(概述入门,UDP, 心跳)

    Apache MINA 2 是一个开发高性能和高可伸缩性网络应用程序的网络应用框架.它提供了一个抽象的事件驱动的异步 API,可以使用 TCP/IP.UDP/IP.串口和虚拟机内部的管道等传输方式.A ...

  6. 高吞吐高并发Java NIO服务的架构(NIO架构及应用之一)

    高吞吐高并发Java NIO服务的架构(NIO架构及应用之一) http://maoyidao.iteye.com/blog/1149015   Java NIO成功的应用在了各种分布式.即时通信和中 ...

  7. Java NIO 学习笔记

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/3344148.html ...

  8. java NIO的多路复用及reactor模式【转载】

    关于java的NIO,以下博客总结的比较详细,适合初学者学习(http://ifeve.com/java-nio-all/) 下面的文字转载自:http://www.blogjava.net/hell ...

  9. JAVA NIO non-blocking模式实现高并发服务器(转)

    原文链接:JAVA NIO non-blocking模式实现高并发服务器 Java自1.4以后,加入了新IO特性,NIO. 号称new IO. NIO带来了non-blocking特性. 这篇文章主要 ...

随机推荐

  1. Asp.net Core WebApi 全局异常类

    通过全局异常类,所有程序中遇到的错误都会被拦截,并友好的返回结果. 1.自定义一个全局异常处理类中间件 using Microsoft.AspNetCore.Http; using Newtonsof ...

  2. 在 Excel 中使用正则表达式进行查找与替换

    在 Excel 中,使用 Alt+F11 快捷键打开 VBA 项目窗口,在左侧的工作表名称上点右键,选择查看代码,即可出出现右侧的代码编辑窗口 在代码窗口中输入以下代码: Private Sub Re ...

  3. drupal7 Views Slideshow 简单教程

    一.下载安装(略) 二.内容类型建立(过程略,首页幻灯),字段建立(过程略)主要有2个字段,图片字段 和 指向链接字段 三.view 1.建立一个新的view,名称为frontbanner 显示为内容 ...

  4. 实现仿知乎的开场动画,图片zoomin的效果,实现原理,没加动效

    知乎等应用的开场动画是:全屏显示一副图像,并以图像的中间为原点,实现放大(也就是zoomin)的动画,让等待的过程不再单调乏味. 最近不是很忙,因此想了下如何实现这种效果,方案是:采用调整imagev ...

  5. Hibernate缓存原理与策略 Hibernate缓存原理:

    Hibernate缓存原理: 对于Hibernate这类ORM而言,缓存显的尤为重要,它是持久层性能提升的关键.简单来讲Hibernate就是对JDBC进行封装,以实现内部状态的管理,OR关系的映射等 ...

  6. ios辅助功能之voiceover实战

      一个元素朗读的内容可分为以下4个部分(4部分按先后顺序朗读) 1. Label:元素的标题 2. Value:元素的值(可选) 3. Traits:元素的特征,即类型,包含: 按钮/链接/搜索框/ ...

  7. ubuntu-15.10-server-i386.iso 安装 Oracle 11gR2 数据库

    注意: ld的版本为 2.25.1,gcc版本为gcc-5 特点: 需要重新安装老版本的 libaio1_0.3.109-2ubuntu?_i386.deb.默认的libaio库有问题,和其默认lib ...

  8. Android中GridView使用总结

    1.http://blog.csdn.net/hellogv/article/details/4567095  基础篇,GridView最基本的用法 2.http://my.eoe.cn/cainia ...

  9. 纯CSS3实现3D特效的iPhone 6动画

    iPhone 6发布不久,屌丝怎能买得起,不过作为程序员,今天看到一个用纯CSS3绘制的iPhone 6,由于CSS3特性的运用,带有点3D的动画特效,大家可以先来看看在线演示效果. 在线演示    ...

  10. 为Eclipse添加Java和Android SDK源代码

    1.添加jdk源码进入eclipse Ctrl + Click -->Attached Source 路径:D:\Program Files\Java\jdk1.8.0_45\src.zip 2 ...