/** * 简介: * java 7 的NIO.2提供了一步Channel支持,这种异步的Channel可以提供更高效的IO, * 这种基于异步的Channel的IO机制也被称为异步IO(AsynchronousIO) * 几种IO流的区别: * 按照POSIX的标准来划分IO,可以把IO分为两类:同步IO和异步IO。对于IO操作可以分为两步: * 1.程序发出IO请求 * 2.完成实际的IO操作 * BIO和NIO的区别是针对于第一步来划分的 * 如果发出的IO会阻塞线程,就是阻塞IO,传统的IO都是这种,也教BIO * 如果发出的IO请求没有阻塞线程,就是非阻塞IO,叫做NIO * * NIO和AIO的区别是针对于第二步来划分的 * 如果实际的IO操作是由操作系统完成的,再将结果返回给应用程序,这就是异步IO,叫做AIO; * 如果实际的IO操作需要程序本身去执行,会阻塞线程,那就是同步IO,叫做NIO * * 一.AsynchronousServerSocketChannel是一个负责监听的Channel,与ServerSocketChannel类似, * 创建可用的AsynchronousServerSocketChannel需要如下两步 * 1.调用它的open()静态方法创建一个未监听端口的AsynchronousServerSocketChannel * 2.调用该对象的bind()方法指定该Channel在指定地址,指定端口监听。 * ->open()方法有两个版本 * 1、open()创建一个默认的AsynchronousServerSocketChannel * 2、open(AsynchronousChannelGroup group):使用指定的AsynchronousChannelGroup来创建AsynchronousServerSocketChannel * * AsynchronousChannelGroup是一个异步Channel的分组管理器,它可以实现资源共享。创建时需要传入一个线程池,该线程池负责 * 两个任务:处理IO事件和触发CompletionHandler // 创建一个线程池 ExecutorService executorService = Executors.newFixedThreadPool(20); // 以指定的线程池来创建一个AsynchronousChannelGroup AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(executorService); // 以指定的线程池来创建一个AsynchronousServerSocketChannel AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open(channelGroup); // 监听特定的端口 serverSocketChannel.bind(new InetSocketAddress(port)); 二、连接 AsynchronousServerSocketChannel创建成功后,可以通过accept()方法来接受来自客户端的连接, accept()方法有如下两个版本 1、Future<AsynchronousSocketChannel> accept():如果程序需要获得连接成功的后返回的AsynchronousSocketChannel ,则应该调用该返回方法的Future对象的get()方法,该方法会阻塞线程,因此这种方式是阻塞的 Future<AsynchronousSocketChannel> future = socketChannel.accept(); // 获取连接返回的AsynchronousSocketChannel AsynchronousSocketChannel channel = future.get(); 2、public abstract <A> void accept(A attachment, CompletionHandler<AsynchronousSocketChannel,? super A> handler):接受客户端的请求,连接成功或失败都会触发 CompletionHandler对象里相应的方法,其中AsynchronousSocketChannel就代表连接成功后返回的Channel 三、处理请求 CompletionHandler 接口,里面有两个方法。 1、void completed(V result, A attachment); 当IO操作完成时触发该方法,第一个参数代表IO操作所返回的对象,第二个参数代表IO操作时传入的附加参数 2、void failed(Throwable exc, A attachment); 当IO操作完成时触发该方法,第一个参数代表IO操作失败时引发的异常或错误,第二个参数代表IO操作时传入的附加参数
public class SimpleAIOServer {
// 定义服务器主机名
private final String hostName = "127.0.0.1";
// 定义一个端口
private static final int port = 9999;
public static void main(String[] args) {
try {
// 1.创建AsynchronousServerSocketChannel对象
AsynchronousServerSocketChannel socketChannel = AsynchronousServerSocketChannel.open();
// 2.指定在特定的地址、端口监听
socketChannel.bind(new InetSocketAddress(port));
while(true){
// 3.采用循环接收来自客户端的连接
Future<AsynchronousSocketChannel> future = socketChannel.accept();
// 获取连接返回的AsynchronousSocketChannel
AsynchronousSocketChannel channel = future.get();
// 执行输出
channel.write(ByteBuffer.wrap("欢迎来到AIO的世界!".getBytes("UTF-8"))).get();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
AsynchronousSocketChannel的用法 1、调用open()静态方法创建一个AsynchronousSocketChannel。也可指定一个分组管理器 2、调用该对象的connect()方法指定一个IP地址和端口号 3、调用该对象的read(),writer()方法进行读写 备注:connect(),read(),writer()这三个对象都有两个版本,一个返回Future对象的版本,一个传入 CompletionHandler 参数的版本。
public class SimpleAIOClient {
// 定义服务器主机名
private static final String hostName = "127.0.0.1";
// 定义一个端口
private static final int port = 9999;
public static void main(String[] args) {
// 用于读取数据的ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
InetSocketAddress inetSocketAddress = new InetSocketAddress(hostName,port);
// 定义实现编码,解码的字符集对象
Charset charset = Charset.forName("UTF-8");
try{
// 1.创建AsynchronousSocketChannel对象
AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
// 2.连接到远程服务器
socketChannel.connect(inetSocketAddress).get();
byteBuffer.clear();
// 3.从socketChannel中读取数据
socketChannel.read(byteBuffer).get();
byteBuffer.flip();
// 将读取的字节转换成字符串
String content = charset.decode(byteBuffer).toString();
System.out.println("服务器信息:"+content);
}catch (Exception e){
e.printStackTrace();
}
}
}
下面是聊天室代码
package com.qingqing.io.net.myaio;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AIOServer {
// 定义服务器主机名
private static final String hostName = "127.0.0.1";
// 定义一个端口
private static final int port = 9999;
// 定义实现编码,解码的字符集对象
private static Charset charset = Charset.forName("UTF-8");
// 用来保存连接进来的客户端连接
private static List<AsynchronousSocketChannel> channelList = new ArrayList<>();
public void startListen() throws Exception{
// 创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(20);
// 以指定的线程池来创建一个AsynchronousChannelGroup
AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(executorService);
// 以指定的线程池来创建一个AsynchronousServerSocketChannel
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open(channelGroup);
// 监听特定的端口
serverSocketChannel.bind(new InetSocketAddress(port));
// 使用CompletionHandler接收来自客户端的连接请求
serverSocketChannel.accept(null,new AcceptHandler(serverSocketChannel));
}
private class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel,Object>{
private AsynchronousServerSocketChannel serverSocketChannel;
// 用于读取数据的ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
public AcceptHandler(AsynchronousServerSocketChannel serverSocketChannel) {
this.serverSocketChannel = serverSocketChannel;
}
/**
* 当实际IO操作完成时触发
* @param result
* @param attachment
*/
@Override
public void completed(AsynchronousSocketChannel result, Object attachment) {
// 记录连接进来的Channel
channelList.add(result);
serverSocketChannel.accept(null,this);
result.read(byteBuffer, null,
new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result1, Object attachment) {
byteBuffer.flip();
// 将buff中的内容转换成字符串
String content = StandardCharsets.UTF_8.decode(byteBuffer).toString();
try {
System.out.println(result.getRemoteAddress()+":"+content);
}catch (Exception e){
e.printStackTrace();
}
// 遍历每个AsynchronousSocketChannel,将收集到的信息写入到Channel中
for(AsynchronousSocketChannel asc:channelList){
try{
asc.write(ByteBuffer.wrap(content.getBytes("utf-8"))).get();
}catch (Exception e){
e.printStackTrace();
}
}
byteBuffer.clear();
result.read(byteBuffer,null,this);
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("读取数据失败:"+exc);
// 从该Channel中读取数据失败,将该Channel删除
channelList.remove(result);
}
}
);
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("连接失败:"+exc);
}
}
public static void main(String[] args) throws Exception{
AIOServer aioServer = new AIOServer();
aioServer.startListen();
System.out.println("服务器已启动!");
new Thread(new Runnable() {
@Override
public void run() {
while (true){
}
}
}).start();
}
}
package com.qingqing.io.net.myaio;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AIOClient {
// 定义服务器主机名
private static final String hostName = "127.0.0.1";
// 定义一个端口
private static final int port = 9999;
final static String UTF_8 = "utf-8";
// 与服务端通信的异步Channel
AsynchronousSocketChannel socketChannel;
// 界面
JFrame mainWin = new JFrame("多人聊天室");
JTextArea jta = new JTextArea(16, 48);
JTextField jtf = new JTextField(40);
JButton sendBn = new JButton("发送");
public void init() {
mainWin.setLayout(new BorderLayout());
jta.setEnabled(false);
mainWin.add(new JScrollPane(jta), BorderLayout.CENTER);
JPanel jp = new JPanel();
jp.add(jtf);
jp.add(sendBn);
// 发送消息的Action,Action是ActionListener的子接口
Action sendAction = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
String content = jtf.getText();
if (content.trim().length() > 0) {
try {
// 将content内容写入到Channel中
socketChannel.write(ByteBuffer.wrap(content.trim().getBytes(UTF_8))).get();
}catch (Exception ex){
ex.printStackTrace();
}
}
// 清空输入框
jtf.setText("");
}
};
// 将按钮与事件绑定
sendBn.addActionListener(sendAction);
jtf.getInputMap().put(KeyStroke.getKeyStroke('\n', InputEvent.CTRL_MASK),"send");
jtf.getActionMap().put("send",sendAction);
mainWin.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWin.add(jp,BorderLayout.SOUTH);
mainWin.pack();
mainWin.setVisible(true);
}
public void connect() throws Exception{
// 用于读取数据的ByteBuffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
// 创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(20);
// 以指定的线程池来创建一个AsynchronousChannelGroup
AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(executorService);
// 以指定的线程池来创建一个AsynchronousSocketChannel
socketChannel = AsynchronousSocketChannel.open(channelGroup);
InetSocketAddress inetSocketAddress = new InetSocketAddress(hostName,port);
// 2.连接到远程服务器
socketChannel.connect(inetSocketAddress).get();
jta.append("---与服务器连接成功---\n");
byteBuffer.clear();
socketChannel.read(byteBuffer, null, new CompletionHandler<Integer, Object>() {
@Override
public void completed(Integer result, Object attachment) {
byteBuffer.flip();
// 将buff中的内容转换成字符串
String content = StandardCharsets.UTF_8.decode(byteBuffer).toString();
// 显示从服务器端读取的数据
jta.append("某人说: "+content+"\n");
byteBuffer.clear();
socketChannel.read(byteBuffer,null,this);
}
@Override
public void failed(Throwable exc, Object attachment) {
System.out.println("读取数据失败:"+exc);
}
});
}
public static void main(String[] args) throws Exception{
AIOClient client = new AIOClient();
client.init();
client.connect();
}
}