1. Java NIO初探
1.1 什么是IO
I/O输入/输出(Input/Output),分为IO设备和IO接口两个部分。 在POSIX兼容的系统上,例如Linux系统,I/O操作可以有多种方式,比如DIO(Direct I/O),AIO(Asynchronous I/O 异步I/O),Memory-Mapped I/O(内存映设I/O)等,不同的I/O方式有不同的实现 方式和性能,在不同的应用中可以按情况选择不同的I/O方式。
IO是主存和外部设备之间拷贝数据的过程。IO的实现是通过操作系统调用IO命令完成的。高级语言对这些IO命令加以封装形成IO api,应用程序通过调用这些api完成网络通信,文件存储,程序的输入输出等。
1.2 Java NIO
Java NIO 是 Java New IO 的简称,实在jdk1.4版本之后提供的新的IO api。该api提供了无阻塞的io访问的实现方式。
Sun 标榜的Java NIO特性如下:
– 为所有的原始类型提供 (Buffer) 缓存支持。
– 字符集编码解码解决方案。
– Channel :一个新的原始 I/O 抽象。
– 支持锁和内存映射文件的文件访问接口。
– 提供多路 (non-bloking) 非阻塞式的高伸缩性网络 I/O 。
1.3 Java NIO 实战
a)现在实用JAVA NIO实现一个文本文件拷贝的过程。
package org.cookie.nio;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
public class NIO {
public static void main(String args[]) throws IOException {
String filePathIn = "c:\\nio_test.sql";
String filePathOut = "d:\\nio_test_n.txt";
File fileOutN = new File("d:\\nio_test_nn.txt");
File fileOut = new File(filePathOut);
if (!fileOut.exists()) {
fileOut.createNewFile();
}
if (!fileOutN.exists()) {
fileOutN.createNewFile();
}
FileInputStream fileInputStream = new FileInputStream(filePathIn);
FileOutputStream fileOutputStream = new FileOutputStream(filePathOut);
FileOutputStream fileOutputStreamN = new FileOutputStream(fileOutN);
FileChannel fileInputChannel = fileInputStream.getChannel(); //从文件流中拿到文件通道
FileChannel fileOutputChannel = fileOutputStream.getChannel();
FileChannel fileOutputChannelN = fileOutputStreamN.getChannel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);//建立一个缓存通道
while (true) {
byteBuffer.clear();//清空通道,byteBuffer变得可写
int r = fileInputChannel.read(byteBuffer);//文件通道向byteBuffer读入数据
if (r == -1) {
break;
}
byteBuffer.flip();//环绕byteBuffer这时候可以将byteBuffer写入到文件通道中
fileOutputChannel.write(byteBuffer);
byteBuffer.flip();//byteBuffer通道可以实现重复读,在读之前需要调用byteBuffer的flip方法
fileOutputChannelN.write(byteBuffer);
byteBuffer.flip();
System.out.println(byteBufferToString(byteBuffer));
}
fileInputStream.close();
fileOutputStream.close();
fileOutputStreamN.close();
}
public static String byteBufferToString(ByteBuffer buffer) {
System.out.println("buffer=" + buffer);
try {
Charset charset = Charset.forName("UTF-8");//声明一个字符集
CharBuffer charBuffer = charset.decode(buffer);//通过字符集对buffer进行编码
System.out.println("charBuffer=" + charBuffer);
System.out.println(charBuffer.toString());
return charBuffer.toString();
} catch (Exception ex) {
ex.printStackTrace();
return "";
}
}
}
b) 通过 Java AIO实现一个简单的服务端,客户端通信
JAVA NIO Server 端
package com.cookie.study.java;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
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.nio.charset.Charset;
import java.util.Iterator;
public class HeartServerNIO {
public static String decode(ByteBuffer byteBuffer){
Charset charset = Charset.forName("UTF-8");
CharBuffer charBuffer = charset.decode(byteBuffer);
return charBuffer.toString();
}
public static void main(String args[])
{
try {
Selector selector = Selector.open();//创建一个selector对象,selector是在Java网络aio中最重要的对象
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();//建立一个ServerSocketChannel
serverSocketChannel.configureBlocking(false);//设置ServerSocketChannel为非阻塞的方式
InetSocketAddress address = new InetSocketAddress(9000);
serverSocketChannel.socket().bind(address);
//注册Selector,通过SelectionKey.OP_ACCEPT
SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true){
int selectedNum = selector.select();//Selects a set of keys whose corresponding channels are ready for I/O operations. 翻译为:选取一下已经准备好IO操作的通道的SelectionKey
System.out.println("Selected Number is :"+selectedNum);
Iterator iter = selector.selectedKeys().iterator();//迭代SelectionKeys
while(iter.hasNext()){
SelectionKey selectedKey = (SelectionKey)iter.next();
if ((selectedKey.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT){// 处理OP_ACCEPT事件
ServerSocketChannel serverChannel = (ServerSocketChannel)selectedKey.channel();
SocketChannel socketChannel = serverChannel.accept();
socketChannel.configureBlocking(false);
SelectionKey readKey = socketChannel.register(selector, SelectionKey.OP_READ);//注册OP_READ事件
iter.remove();
}else if ( (selectedKey.readyOps()&SelectionKey.OP_READ) == SelectionKey.OP_READ ){//处理OP_READ事件
ByteBuffer buffer = ByteBuffer.allocate(1024);
SocketChannel socketChannel = (SocketChannel)selectedKey.channel();
while (true){
buffer.clear();
int i=socketChannel.read(buffer);//将socket数据读入byteBuffer中
if (i == -1) break;
if (i == 0) break;
buffer.flip();//环绕byteBuffer,准备读取buffer数据
socketChannel.write(buffer);
buffer.flip();//需要再次读取时,需要再次调用buffer的环绕方法
System.out.println(decode(buffer));
}
iter.remove();
}
}
}
} catch (ClosedChannelException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Java NIO Client 端
package com.cookie.study.java;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
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;
import java.util.Set;
public class HeartClientNIO extends Frame {
/* 标识数字 */
private static int flag = 0;
/* 缓冲区大小 */
private static int BLOCK = 4096;
/* 接受数据缓冲区 */
private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);
/* 发送数据缓冲区 */
private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);
/*
* 成员方法出场...
*/
private TextField tfText;
private TextArea taContent;
private TFListener tfListener = new TFListener();
/**
* 注意,入口... ^^
*
* @param args
*/
public static void main(String[] args) {
new HeartClientNIO().launchFrame();
}
/**
* Loading GU
*/
public void launchFrame() {
tfText = new TextField();
taContent = new TextArea();
this.setSize(300, 300);
this.setLocation(300, 300);
this.tfText.addActionListener(this.tfListener);
this.add(tfText, BorderLayout.SOUTH);
this.add(taContent, BorderLayout.NORTH);
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
this.pack();
this.connect();
this.setVisible(true);
}
/**
* 我在努力地连接服务器中...
*/
public void connect() {
try {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
Selector selector = Selector.open();
socketChannel.register(selector, SelectionKey.OP_CONNECT);
InetSocketAddress inetSocketAddress = new InetSocketAddress(
"localhost", 9000);
socketChannel.connect(inetSocketAddress);
this.tfListener.setSelector(selector);
Set<SelectionKey> selectionKeys;
Iterator<SelectionKey> iterator;
SelectionKey selectionKey;
SocketChannel client;
String receiveText;
String sendText;
int count = 0;
// 选择一组键,其相应的通道已为 I/O 操作准备就绪。
// 此方法执行处于阻塞模式的选择操作。
selector.select();
// 返回此选择器的已选择键集。
selectionKeys = selector.selectedKeys();
System.out.println(selectionKeys.size());
iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
selectionKey = iterator.next();
if (selectionKey.isConnectable()) {
System.out.println("client connect");
client = (SocketChannel) selectionKey.channel();
// 判断此通道上是否正在进行连接操作。
// 完成套接字通道的连接过程。
if (client.isConnectionPending()) {
client.finishConnect();
System.out.println("完成连接!");
sendbuffer.clear();
sendbuffer.put("Hello,Server".getBytes("UTF-8"));
sendbuffer.flip();
client.write(sendbuffer);
}
client.register(selector, SelectionKey.OP_READ);
}
}
selectionKeys.clear();
new Thread(new ReadProcess(selector)).start();
} catch (UnknownHostException e) {
System.out.println("UnknownHostException");
e.printStackTrace();
} catch (IOException e) {
System.out.println("IOException");
e.printStackTrace();
} finally {
// 关闭啥尼...
}
}
/**
* 额不会傻等着tfText(TextField tfText的监听器类)
*/
class TFListener implements ActionListener {
private String str;
private Selector selector;
@Override
public void actionPerformed(ActionEvent e) {
str = tfText.getText().trim();
tfText.setText("");
try {
if (selector != null) {
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
if (selectionKey.isWritable()) {
sendbuffer.clear();
SocketChannel client = (SocketChannel) selectionKey.channel();
String sendText = "message from client--" + (flag++)+str;
sendbuffer.put(sendText.getBytes("UTF-8"));
// 将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位
sendbuffer.flip();
client.write(sendbuffer);
System.out.println("客户端向服务器端发送数据--:" + sendText);
client.register(selector, SelectionKey.OP_READ);
}
}
selectionKeys.clear();
}
} catch (ClosedChannelException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
public void setSelector(Selector selector) {
this.selector = selector;
}
}
public class ReadProcess implements Runnable{
private Selector selector;
public ReadProcess(Selector selector){
this.selector = selector;
}
public void run() {
while(true){
try {
selector.select();
Set<SelectionKey> selectionKeys = this.selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
if(selectionKey.isReadable()){
SocketChannel client = (SocketChannel) selectionKey.channel();
// 将缓冲区清空以备下次读取
receivebuffer.clear();
// 读取服务器发送来的数据到缓冲区中
int count = client.read(receivebuffer);
if (count > 0) {
String receiveText = new String(receivebuffer.array(), 0,
count,"UTF-8");
System.out.println("客户端接受服务器端数据--:" + receiveText);
taContent.setText(taContent.getText()+"\n"+receiveText);
client.register(selector, SelectionKey.OP_WRITE);
}
}
}
selectionKeys.clear();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1036

被折叠的 条评论
为什么被折叠?



