粗汉之BIO/NIO/AIO实战

粗汉之IO实战

1、BIO、NIO、AIO之间的根本区别。
BIO

​ 同步阻塞I/O,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制来改善。BIO方式适用于连接数目比较小且固定的架构,这种方式对服务端资源要求比较高,并发局限于应用中,在jdk1.4以前是唯一的io现在,但程序直观简单易理解

NIO

​ 同步非阻塞I/O,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,jdk1,4开始支持 【线程池】

AIO

异步非阻塞I/O,服务器实现模式为一个有效请求一个线程,客户端的IO请求都是由操作系统先完成了再通知服务器用其启动线程进行处理。AIO方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,jdk1.7开始支持。 【事件驱动、回调】

因为由操作系统来回调,所以操作系统的性能,决定IO性能(存在兼容问题)

IO和NIO区别

​ IO面向流,NIO面向缓冲区

​ IO的各种流是阻塞的,NIO是非阻塞模式

​ Java NIO的选择允许一个单独的线程来监视多个输入通道,可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入或选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道

2、BIO代码简易实现
服务端
public class BIOClient {
    public static void main(String[] args) {
        try (Socket socket = new Socket("localhost", 8080);
             BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
             BufferedReader sin = new BufferedReader(new InputStreamReader(System.in))) {

            String result = sin.readLine();
            while (!"bye".equals(result)) {
                out.println(result);

                System.out.println("server : " + in.readLine());
                result = sin.readLine();
            }

        } catch (IOException e) {
            e.getStackTrace();
        }
    }
}
客户端
public class BIOServer {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(8080);
             Socket socket = serverSocket.accept();
             BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
             BufferedReader sin = new BufferedReader(new InputStreamReader(System.in))) {

            System.out.println("client : " + in.readLine());

            String result = sin.readLine();
            while (!"bye".equals(result)) {
                out.println(result);

                System.out.println("client : " + in.readLine());
                result = sin.readLine();
            }


        } catch (IOException e) {
            e.getStackTrace();
        }
    }
}
3、NIO代码简易实现。

学习之前,先了解几种SelectionKey状态

SelectionKey.OP_ACCEPT 如果注册的时候设置的这个,那么此通道就是端口监听

SelectionKey.OP_CONNECT 如果注册的时候设置的这个,那么此通道就是可连接

SelectionKey.OP_READ 如果注册的时候设置的这个,那么此通道就是可读

SelectionKey.OP_WRITE 如果注册的时候设置的这个,那么此通道就是待写

再来了解几个概念

Selector选择器,说白了就是公司门口的接待

SelectionKey 包装的通道+状态

SocketChannel 就是通道了,也就是有需要的话就把自己注册到Selector里面,并且标注SelectorKey状态

​ a、serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

​ b、socketChannel.register(selector, SelectionKey.OP_READ);

所以呢?所以呢?

​ 说白了就是去遍历Selector然后取出可用的SelectionKey取出来看是什么情况,然后根据状态不同做不同处理,所以有点麻烦,也就是因为麻烦所以才出来了NIO框架嘛

​ 可以使用NIO实现 群聊哈

服务端
public class Server {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8080));
        serverSocketChannel.configureBlocking(false);
        //注册到selector中,设置事件为监听事件
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            //selector.select()是阻塞的
            if (selector.select() != 0) {

                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();

                while (iterator.hasNext()) {
                    SelectionKey selectionKey = iterator.next();
                    // 移除 ,避免重复消费
                    iterator.remove();

                    SocketChannel socketChannel = null;
                    // 如果是请求接入
                    if (selectionKey.isAcceptable()) {
                        socketChannel = acceptSelectionKey(selectionKey, serverSocketChannel.accept());
                    }

                    // 如果是可读
                    if (selectionKey.isReadable()) {
                        socketChannel = readSelectionKey(selectionKey, true);
                    }

                    // 再次注册,并且监听可读事件
                    if (socketChannel != null) {
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }
                }
            }
        }
    }

    /**
     * 监听请求
     */
    private static SocketChannel acceptSelectionKey(SelectionKey selectionKey, SocketChannel socketChannel) throws IOException {
        // 设置成非阻塞
        socketChannel.configureBlocking(false);

        // 回复客户端消息
        socketChannel.write(Charset.defaultCharset().encode("您与聊天室其他人都不是朋友关系,请注意隐私安全!"));
        return socketChannel;
    }

    /**
     * 读取消息
     */
    public static SocketChannel readSelectionKey(SelectionKey selectionKey, boolean isBoardCase) throws IOException {
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();

        ByteBuffer byteBuffer = ByteBuffer.allocate(1);
        StringBuffer result = new StringBuffer();

        while (socketChannel.read(byteBuffer) > 0) {
            //转成读模式
            byteBuffer.flip();
            result.append(Charset.defaultCharset().decode(byteBuffer));
            byteBuffer.clear();
        }

        if (result.toString().length() > 0 && isBoardCase) {
            //可以看是否广播给其他变量
            System.out.println(result);
        }
        return socketChannel;
    }
}
客户端
public class Client {
    // 这里也可以用nio写法和服务端差不多
    // 不过在发消息的时候需要用到线程或线程池,这样就可以不影响自己接受消息,达到聊天的效果
    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("localhost", 8080));
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            String result = scanner.next();
            if (result != null && result.length() > 0) {
                socketChannel.write(Charset.defaultCharset().encode("client-李四:" +result));
            }
        }
    }
}
4、AIO代码简易实现
服务端
package org.example;

import java.io.IOException;
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.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * AIO 服务端
 */
public class AIOServer {
    private final int port;

    public static void main(String args[]) {
        int port = 8000;
        new AIOServer(port);
    }

    public AIOServer(int port) {
        this.port = port;
        listen();
    }

    private void listen() {
        try {
            ExecutorService executorService = Executors.newCachedThreadPool();
            AsynchronousChannelGroup threadGroup = AsynchronousChannelGroup.withCachedThreadPool(executorService, 1);
            // 工作线程,用来侦听回调的,事件响应的时候需要回调
            final AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(threadGroup);
            server.bind(new InetSocketAddress(port));
            System.out.println("服务已启动,监听端口" + port);
            server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
                final ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
				// 由操作系统来触发回调的
                @Override
                public void completed(AsynchronousSocketChannel result, Object attachment) {
                    System.out.println("IO 操作成功,开始获取数据");
                    try {
                        buffer.clear();
                        result.read(buffer).get();
                        buffer.flip();
                        result.write(buffer);
                        buffer.flip();
                    } catch (Exception e) {
                        System.out.println(e.toString());
                    } finally {
                        try {
                            result.close();
                            server.accept(null, this);
                        } catch (Exception e) {
                            System.out.println(e.toString());
                        }
                    }
                    System.out.println("操作完成");
                }

                @Override
                public void failed(Throwable exc, Object attachment) {
                    System.out.println("IO 操作是失败: " + exc);
                }
            });
            try {
                Thread.sleep(Integer.MAX_VALUE);
            } catch (InterruptedException ex) {
                System.out.println(ex);
            }
        } catch (IOException e) {
            System.out.println(e);
        }
    }
}
客户端
package org.example;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

/**
 * AIO 客户端
 */
public class AIOClient {
    private final AsynchronousSocketChannel client;

    public AIOClient() throws Exception {
        client = AsynchronousSocketChannel.open();
    }

    public void connect(String host, int port) throws Exception {
        client.connect(new InetSocketAddress(host, port), null, new CompletionHandler<Void, Void>() {
            // 由操作系统来触发回调的
            @Override
            public void completed(Void result, Void attachment) {
                try {
                    client.write(ByteBuffer.wrap("这是一条测试数据".getBytes())).get();
                    System.out.println("已发送至服务器");
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }

            @Override
            public void failed(Throwable exc, Void attachment) {
                exc.printStackTrace();
            }
        });
        final ByteBuffer bb = ByteBuffer.allocate(1024);
        client.read(bb, null, new CompletionHandler<Integer, Object>() {
                    @Override
                    public void completed(Integer result, Object attachment) {
                        System.out.println("IO 操作完成" + result);
                        System.out.println("获取反馈结果" + new String(bb.array()));
                    }

                    @Override
                    public void failed(Throwable exc, Object attachment) {
                        exc.printStackTrace();
                    }
                }
        );
        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException ex) {
            System.out.println(ex);
        }
    }

    public static void main(String args[]) throws Exception {
        new AIOClient().connect("localhost", 8000);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值