JAVA的BIO、NIO和AIO

通常IO操作包括对磁盘的读写,socket网络的读写和外部连接设备的读写,在讲解java的io之前需要了解,同步、异步、阻塞和非阻塞的概念

网络IO请求完整的过程分为两个阶段,第一个发送io请求指令,第二阶段响应结果的处理。同步异步主要发生在第二阶段故同步异步的差异就发生在第二阶段,阻塞非阻塞主要发生在第一阶段。

同步:在第二阶段服务器响应结果,需要客户端先线程主动去询问是否完成和响应数据

异步:在第二阶段服务器响应结果,系统内核通过selector或epoll基于事件驱动,通过回调把处理结果推给客户端线程

阻塞:在第一阶段发送io请求指令后,用户线程只能阻塞状态等待io请求的响应

非阻塞:在第一阶段发送io请求指令后,用户线程可以继续去做其他事情

BIO

BIO传统的io,同步阻塞io,客户线程提交IO请求后,线程阻塞,只能等待服务断io数据的返回,InputStream和OutputStream直流读取,网络BIO使用传统的socket套接字完成网络io通信。

实例代码:

Server服务端:

package io.bio;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author shengtao
 * @Description:
 * @date 2019/03/29 15:49
 */
public class Server {
    public static final int port = 8081;//监听的端口号

    public static void main(String[] args) {
        System.out.println("Server start...\n");
        Server server = new Server();
        server.init();
    }

    public void init() {
        try {
            //创建一个ServerSocket,这里可以指定连接请求的队列长度
            //new ServerSocket(port,3);意味着当队列中有3个连接请求是,如果Client再请求连接,就会被Server拒绝
            ServerSocket serverSocket = new ServerSocket(port);
            while (true) {
                //从请求队列中取出一个连接
                Socket client = serverSocket.accept();
                // 处理这次连接
                new HandlerThread(client);
            }
        } catch (Exception e) {
            System.out.println("服务器异常: " + e.getMessage());
        }
    }

    private class HandlerThread implements Runnable {
        private Socket socket;
        public HandlerThread(Socket client) {
            socket = client;
            new Thread(this).start();
        }

        public void run() {
            try {
                // 读取客户端数据
                BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String clientInputStr = input.readLine();//这里要注意和客户端输出流的写方法对应,否则会抛 EOFException
                // 处理客户端数据
                System.out.println("客户端发过来的内容:" + clientInputStr);

                // 向客户端回复信息
                PrintStream out = new PrintStream(socket.getOutputStream());
                System.out.print("请输入:\t");
                // 发送键盘输入的一行
                String s = new BufferedReader(new InputStreamReader(System.in)).readLine();
                out.println(s);

                out.close();
                input.close();
            } catch (Exception e) {
                System.out.println("服务器 run 异常: " + e.getMessage());
            } finally {
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (Exception e) {
                        socket = null;
                        System.out.println("服务端 finally 异常:" + e.getMessage());
                    }
                }
            }
        }
    }
}

Client客户端:

package io.bio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;

/**
 * @author shengtao
 * @Description:
 * @date 2019/03/29 15:58
 */

public class Client {
    public static final int port = 8081;
    public static final String host = "localhost";
    public static void main(String[] args) {
        System.out.println("Client Start...");
        while (true) {
            Socket socket = null;
            try {
                //创建一个流套接字并将其连接到指定主机上的指定端口号
                socket = new Socket(host,port);

                //读取服务器端数据
                BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                //向服务器端发送数据
                PrintStream out = new PrintStream(socket.getOutputStream());
                System.out.print("请输入: \t");
                String str = new BufferedReader(new InputStreamReader(System.in)).readLine();
                out.println(str);

                String ret = input.readLine();
                System.out.println("服务器端返回过来的是: " + ret);
                // 如接收到 "OK" 则断开连接
                if ("OK".equals(ret)) {
                    System.out.println("客户端将关闭连接");
                    Thread.sleep(500);
                    break;
                }

                out.close();
                input.close();
            } catch (Exception e) {
                System.out.println("客户端异常:" + e.getMessage());
            } finally {
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        socket = null;
                        System.out.println("客户端 finally 异常:" + e.getMessage());
                    }
                }
            }
        }
    }
}

运行结果:

Server端:

Server start...

客户端发过来的内容:你好
请输入:	你好
客户端发过来的内容:认识你很高兴
请输入:	OK

Client端:

Client Start...
请输入: 	你好
服务器端返回过来的是: 你好
请输入: 	认识你很高兴
服务器端返回过来的是: OK
客户端将关闭连接

Process finished with exit code 0

NIO

同步非阻塞IO,(1)BIO中引入了Buffer缓冲区,网络IO请求读写都是在作用在Buffer缓冲区,然后Buffe缓冲区与内核指令处理是异步完成的。(2)BIO引入了通道的概念,与传统的IO流不一样,传统的IO流是单向的,输入输出是两个分开的流,而NIO引入的通道则是双向的,客户端与服务端(认证后)建立通道后,客户端可以和服务端可以通过此通道进行数据通信。(3)BIO引入了IO复用Selector组件,轮询查看注册在监听器上的客户端连接,如果客户端有读、写、连接和断开事件发生,IO复用器监听到会申请线程去处理。(4)BIO是基于事件驱动的IO。

NIOServer端:

package io.nio;

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;

/**
 * @author shengtao
 * @Description:
 * @date 2019/03/29 19:24
 */
public class NIOServer
{
    /**
     * 选择器
     */
    private Selector selector;

    /**
     * 通道
     */
    ServerSocketChannel serverSocketChannel;

    public void initServer(int port) throws IOException
    {
        //打开一个通道
        serverSocketChannel = ServerSocketChannel.open();

        //通道设置非阻塞
        serverSocketChannel.configureBlocking(false);

        //绑定端口号
        serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1", port));

        //注册
        this.selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    public void listen() throws IOException
    {
        System.out.println("server started succeed!");

        while (true)
        {
            selector.select();
            Iterator<SelectionKey> ite = selector.selectedKeys().iterator();
            while (ite.hasNext())
            {
                SelectionKey key = ite.next();
                if (key.isAcceptable())
                {
                    SocketChannel channel = serverSocketChannel.accept();
                    channel.configureBlocking(false);
                    channel.register(selector, SelectionKey.OP_READ);
                }
                else if (key.isReadable())
                {
                    recvAndReply(key);
                }
                ite.remove();
            }
        }
    }

    public void recvAndReply(SelectionKey key) throws IOException
    {
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(256);
        int i = channel.read(buffer);
        if (i != -1)
        {
            String msg = new String(buffer.array()).trim();
            System.out.println("NIO server received message =  " + msg);
            System.out.println("NIO server reply =  " + msg);
            channel.write(ByteBuffer.wrap( msg.getBytes()));
        }
        else
        {
            channel.close();
        }
    }

    public static void main(String[] args) throws IOException
    {
        NIOServer server = new NIOServer();
        server.initServer(8081);
        server.listen();
    }
}

NIOClient客户端:

package io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

/**
 * @author shengtao
 * @Description:
 * @date 2019/03/29 19:27
 */
public class NIOClient
{
    /**
     * 通道
     */
    SocketChannel channel;

    public void initClient(String host, int port) throws IOException
    {
        //构造socket连接
        InetSocketAddress servAddr = new InetSocketAddress(host, port);

        //打开连接
        this.channel = SocketChannel.open(servAddr);
    }

    public void sendAndRecv(String words) throws IOException
    {
        byte[] msg = new String(words).getBytes();
        ByteBuffer buffer = ByteBuffer.wrap(msg);
        System.out.println("Client sending: " + words);
        channel.write(buffer);
        buffer.clear();
        channel.read(buffer);
        System.out.println("Client received: " + new String(buffer.array()).trim());

        channel.close();
    }

    public static void main(String[] args) throws IOException
    {
        NIOClient client = new NIOClient();
        client.initClient("localhost", 8081);
        client.sendAndRecv("I am a client");
    }
}

执行结果:

NIOServer端:

server started succeed!
NIO server received message =  I am a client
NIO server reply =  I am a client

Process finished with exit code 1

NIOClient端:

Client sending: I am a client
Client received: I am a client

Process finished with exit code 0

AIO

AIO异步非阻塞IO,是JDK1.7引入的NIO2模型,在java的java.nio包下,select多路复用IO底层调用系统的epoll事件驱动模型实现的,使用了NIO1中的buffer缓冲区,双向通道,事件驱动,IO复用器,外层包裹的线程组,一个线程轮询事件,接受事件用线程组中的线程处理客户端读写事件,客户端发起IO请求,客户端线程不会阻塞可以处理其他任务,服务端内核接受IO事件请求,处理IO事件后服务断回调客户端的ahndler对象的complated函数,故被称为异步非阻塞IO。

AIOServer端:

package io.aio;

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.nio.charset.Charset;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;

/**
 * @author shengtao
 * @Description:
 * @date 2019/03/29 20:03
 */
public class AIOServer implements Runnable{

    private int port = 8081;
    private int threadSize = 10;
    protected AsynchronousChannelGroup asynchronousChannelGroup;
    protected AsynchronousServerSocketChannel serverChannel;

    public AIOServer(){
        init();
    }

    private void init(){
        try {
            asynchronousChannelGroup = AsynchronousChannelGroup.withCachedThreadPool(Executors.newCachedThreadPool(),threadSize);
            serverChannel = AsynchronousServerSocketChannel.open(asynchronousChannelGroup);
            serverChannel.bind(new InetSocketAddress(port));
            System.out.println("aio socket channel open on port:"+port);
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("asynchronousChannelGroup init error !");
        }
    }

    @Override
    public void run() {
        try {
            if(serverChannel == null){
                return;
            }else {
                serverChannel.accept(this, new CompletionHandler<AsynchronousSocketChannel, AIOServer>() {
                    final ByteBuffer echoBuffer = ByteBuffer.allocateDirect(1024);
                    @Override
                    public void completed(AsynchronousSocketChannel result, AIOServer attachment) {
                        System.out.println("==============================================================");
                        try {
                            System.out.println("client host: " + result.getRemoteAddress());
                            echoBuffer.clear();
                            result.read(echoBuffer).get();
                            echoBuffer.flip();
                            System.out.println("received : " + Charset.defaultCharset().decode(echoBuffer));
                            result.write(ByteBuffer.wrap("hello client".getBytes()));
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            attachment.serverChannel.accept(attachment, this);// 监听新的请求,递归调用。
                        }
                    }

                    @Override
                    public void failed(Throwable exc, AIOServer attachment) {
                        System.out.println("received failed");
                        exc.printStackTrace();
                        attachment.serverChannel.accept(attachment, this);
                    }
                });
            }

            System.in.read();//线程阻塞
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args){
        AIOServer aioServer = new AIOServer();
        new Thread(aioServer).start();
    }
}

AIOClient客户端:

package io.aio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;

/**
 * @author shengtao
 * @Description:
 * @date 2019/03/29 20:21
 */
public class AIOClient implements Runnable {

    private AsynchronousSocketChannel client;
    private String host;
    private int port;
    public AIOClient(String host, int port) throws IOException {
        this.client = AsynchronousSocketChannel.open();
        this.host = host;
        this.port = port;
    }


    @Override
    public void run() {

        client.connect(new InetSocketAddress("127.0.0.1", 8081),null, new CompletionHandler<Void,Object>() {

            @Override
            public void completed(Void result, Object attachment) {
                String msg = "hello server";
                client.write(ByteBuffer.wrap(msg.getBytes()));
                System.out.println("client send data:" + msg);
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("client send faield");
            }
        });

        final ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        client.read(byteBuffer, this, new CompletionHandler<Integer, AIOClient>() {
            @Override
            public void completed(Integer result, AIOClient attachment) {
                System.out.println("content length:"+result);
                System.out.println("client read data: " + new String(byteBuffer.array()));
            }

            @Override
            public void failed(Throwable exc, AIOClient attachment) {
                System.out.println("read faield");
            }
        });

    }

    public static void main(String[] args){
        try {
            new Thread(new AIOClient("127.0.0.1", 8081)).start();
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

执行结果:

服务端:

aio socket channel open on port:8081
==============================================================
client host: /127.0.0.1:61335
received : hello server

客户端:

client send data:hello server
content length:12
client read data: hello client

总结:虽然我们工作中不经常会直接使用BIO和AIO网络IO模型,但是对于这些只是深入了解会对我们学习netty和mina这样的基于事件驱动的高性能网络架构很有帮助。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值