基于AIO的聊天室

/**
 * 简介:
 * 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();
    }
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值