一、bio(blocking io,也被称作old io)

同步阻塞模型,一个客户端连接对应一个处理线程

对于每一个新的网络连接都会分配给一个线程,每隔线程都独立处理自己负责的输入和输出, 也被称为connection per thread模式

缺点:

1、io代码里read操作是阻塞操作,如果连接不做数据读写操作会导致线程阻塞,浪费资源

2、如果线程很多,会导致服务器线程太多,压力太大,比如c10k问题

所谓c10k问题,指的是服务器同时支持成千上万个客户端的问题,也就是concurrent 10 000 connection

应用场景: bio 方式适用于连接数目比较小且固定的架构, 这种方式对服务器资源要求比较高, 但程序简单易理解。

示例代码如下:

bio服务端

import java.io.ioexception;
import java.net.serversocket;
import java.net.socket;

/**
 * @title:bio的服务端
 * @author:wangchenggong
 * @date 2021/4/13 9:41
 * @description
 * @version
 */
public class socketserver {

    public static void main(string[] args) throws ioexception {
        serversocket serversocket = new serversocket(9000);
        while (true){
            system.out.println("等待连接...");
            socket clientsocket = serversocket.accept();
            system.out.println("客户端"+clientsocket.getremotesocketaddress()+"连接了!");

            handle(clientsocket);
        }

    }

    private static void handle(socket clientsocket)  throws ioexception{
        byte[] bytes = new byte[1024];
        int read = clientsocket.getinputstream().read(bytes);
        system.out.println("read 客户端"+clientsocket.getremotesocketaddress()+"数据完毕");
        if(read != -1){
            system.out.println("接收到客户端的数据:" + new string(bytes, 0, read));
        }
        clientsocket.getoutputstream().write("helloclient".getbytes());
        clientsocket.getoutputstream().flush();
    }

}

bio客户端

import java.io.ioexception;
import java.net.socket;

/**
 * @title:bio的客户端
 * @author:wangchenggong
 * @date 2021/4/13 9:49
 * @description
 * @version
 */
public class socketclient {

    public static void main(string[] args) throws ioexception {

        socket socket = new socket("localhost", 9000);
        //向服务端发送数据
        socket.getoutputstream().write("helloserver".getbytes());
        socket.getoutputstream().flush();
        system.out.println("向服务端发送数据结束");

        byte[] bytes = new byte[1024];
        //接收服务端回传的数据
        socket.getinputstream().read(bytes);

        system.out.println("接收到服务端的数据:" + new string(bytes));
        socket.close();
    }
}

二、nio(non blocking io,本意也作new io)

同步非阻塞,服务器实现模式为 一个线程可以处理多个连接请求(连接),客户端发送的连接请求都会注册到多路复用器selector上,多路复用器轮询到连接有io请求就进行处理,是在jdk1.4开始引入的。

应用场景:nio方式适合连接数目多且连接比较短(轻操作)的架构,比如聊天服务器、弹幕系统、服务器之间通讯,编程相对复杂。

nio 有三大核心组件: channel(通道), buffer(缓冲区),selector(多路复用器)

1.channel类似于流,每个channel对应一个buffer缓冲区,buffer底层就是个数组

2.channel 会注册到selector上,由selector根据channel读写事件的发生将其交由某个空闲的线程处理

3.nio的buffer和channel都是可读也可写的。

nio的代码示例有两个

没有引入多路复用器的nio

服务端

import java.io.ioexception;
import java.net.inetsocketaddress;
import java.nio.bytebuffer;
import java.nio.channels.serversocketchannel;
import java.nio.channels.socketchannel;
import java.util.arraylist;
import java.util.iterator;
import java.util.list;

/**
 * @title:nio服务端
 * @author:wangchenggong
 * @date 2021/4/14 11:04
 * @description
 * @version
 */
public class nioserver {

    /**
     * 保存客户端连接
     */
    static list<socketchannel> channellist = new arraylist<>();

    public static void main(string[] args) throws ioexception {
        //创建nio serversocketchannel
        serversocketchannel serversocket = serversocketchannel.open();
        serversocket.socket().bind(new inetsocketaddress(9000));
        //设置serversocketchannel为非阻塞
        serversocket.configureblocking(false);
        system.out.println("nio服务启动成功");

        while(true){
            //非阻塞模式accept方法不会阻塞
            /// nio的非阻塞是由操作系统内部实现的,底层调用了linux内核的accept函数
            socketchannel socketchannel = serversocket.accept();
            if(socketchannel != null){
                system.out.println("连接成功");
                socketchannel.configureblocking(false);
                channellist.add(socketchannel);
            }

            iterator<socketchannel> iterator = channellist.iterator();
            while(iterator.hasnext()){
                socketchannel sc = iterator.next();
                bytebuffer bytebuffer = bytebuffer.allocate(128);
                //非阻塞模式read方法不会阻塞
                int len = sc.read(bytebuffer);

                if(len > 0){
                    system.out.println("接收到消息:" + new string(bytebuffer.array()));
                }else if(len == -1){
                    iterator.remove();
                    system.out.println("客户端断开连接");
                }
            }

        }
    }
}

客户端

import java.io.ioexception;
import java.net.inetsocketaddress;
import java.nio.bytebuffer;
import java.nio.channels.socketchannel;

/**
 * @title:nio客户端
 * @author:wangchenggong
 * @date 2021/4/14 11:36
 * @description
 * @version
 */
public class nioclient {

    public static void main(string[] args) throws ioexception {

        socketchannel socketchannel=socketchannel.open(new inetsocketaddress("localhost", 9000));
        socketchannel.configureblocking(false);


        bytebuffer writebuffer=bytebuffer.wrap("helloserver1".getbytes());
        socketchannel.write(writebuffer);
        system.out.println("向服务端发送数据1结束");

        writebuffer = bytebuffer.wrap("helloserver2".getbytes());
        socketchannel.write(writebuffer);
        system.out.println("向服务端发送数据2结束");

        writebuffer = bytebuffer.wrap("helloserver3".getbytes());
        socketchannel.write(writebuffer);
        system.out.println("向服务端发送数据3结束");
    }


}

引入了多路复用器的nio

服务端

import java.io.ioexception;
import java.net.inetsocketaddress;
import java.nio.bytebuffer;
import java.nio.channels.*;
import java.util.iterator;
import java.util.set;

/**
 * @title:引入多路复用器后的nio服务端
 * @author:wangchenggong
 * @date 2021/4/14 13:57
 * @description
 * selectionkey.op_accept —— 接收连接继续事件,表示服务器监听到了客户连接,服务器可以接收这个连接了
 * selectionkey.op_connect —— 连接就绪事件,表示客户与服务器的连接已经建立成功
 * selectionkey.op_read —— 读就绪事件,表示通道中已经有了可读的数据,可以执行读操作了(通道目前有数据,可以进行读操作了)
 * selectionkey.op_write —— 写就绪事件,表示已经可以向通道写数据了(通道目前可以用于写操作)
 *
 * 1.当向通道中注册selectionkey.op_read事件后,如果客户端有向缓存中write数据,下次轮询时,则会 isreadable()=true;
 *
 * 2.当向通道中注册selectionkey.op_write事件后,这时你会发现当前轮询线程中iswritable()一直为true,如果不设置为其他事件
 * @version
 */
public class nioselectorserver {

    public static void main(string[] args) throws ioexception {

        /**
         * 创建server端,并且向多路复用器注册,让多路复用器监听连接事件
         */
        //创建serversocketchannel
        serversocketchannel serversocket = serversocketchannel.open();
        serversocket.socket().bind(new inetsocketaddress(9000));
        //设置serversocketchannel为非阻塞
        serversocket.configureblocking(false);
        //打开selector处理channel,即创建epoll
        selector selector = selector.open();
        //把serversocketchannel注册到selector上,并且selector对客户端的accept连接操作感兴趣
        serversocket.register(selector, selectionkey.op_accept);
        system.out.println("nioselectorserver服务启动成功");


        while(true){
            //阻塞等待需要处理的事件发生
            selector.select();

            //获取selector中注册的全部事件的selectionkey实例
            set<selectionkey> selectionkeys = selector.selectedkeys();
            iterator<selectionkey> iterator = selectionkeys.iterator();

            //遍历selectionkeys,对事件进行处理
            while (iterator.hasnext()){
                selectionkey key = iterator.next();
                //如果是op_accept事件,则进行连接和事件注册
                if(key.isacceptable()){
                    serversocketchannel serversocketchannel = (serversocketchannel) key.channel();
                    //接受客户端的连接
                    socketchannel socketchannel = serversocketchannel.accept();
                    socketchannel.configureblocking(false);
                    //把socketchannel注册到selector上,并且selector对客户端的read操作(即读取来自客户端的消息)感兴趣
                    socketchannel.register(selector, selectionkey.op_read);
                    system.out.println("客户端"+socketchannel.getremoteaddress()+"连接成功!");

                }else if(key.isreadable()){
                    socketchannel socketchannel = (socketchannel) key.channel();
                    bytebuffer bytebuffer = bytebuffer.allocate(128);
                    int len = socketchannel.read(bytebuffer);
                    if(len > 0){
                        system.out.println("接收到客户端"+socketchannel.getremoteaddress()+"发来的消息,消息内容为:"+new string(bytebuffer.array()));
                    }else if(len == -1){
                        system.out.println("客户端断开连接");
                        //关闭该客户端
                        socketchannel.close();
                    }
                }
                //从事件集合里删除本次处理的key,防止下次select重复处理
                iterator.remove();
            }

        }

        /**
         * nioselectorserver服务启动成功
         * 客户端/127.0.0.1:57070连接成功!
         * 接收到客户端/127.0.0.1:57070发来的消息,消息内容为:helloserver
         * 客户端/127.0.0.1:57121连接成功!
         * 接收到客户端/127.0.0.1:57121发来的消息,消息内容为:helloserver
         */

    }
}

客户端

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.socketchannel;
import java.util.iterator;
import java.util.set;

/**
 * @title:引入多路复用器后的nio客户端
 * @author:wangchenggong
 * @date 2021/4/14 14:39
 * @description
 * @version
 */
public class nioselectorclient {

    public static void main(string[] args) throws ioexception {

        socketchannel socketchannel = socketchannel.open();
        socketchannel.configureblocking(false);
        selector selector = selector.open();
        //要先向多路复用器注册,然后才可以跟服务端进行连接
        socketchannel.register(selector, selectionkey.op_connect);
        socketchannel.connect(new inetsocketaddress("localhost", 9000));

        while (true){
            selector.select();
            set<selectionkey> keys = selector.selectedkeys();
            iterator<selectionkey> iterator = keys.iterator();
            while (iterator.hasnext()){
                selectionkey key = iterator.next();
                iterator.remove();
                if (key.isconnectable()){
                    socketchannel sc = (socketchannel) key.channel();
                    if (sc.finishconnect()){
                        system.out.println("服务器连接成功");

                        bytebuffer writebuffer=bytebuffer.wrap("helloserver".getbytes());
                        sc.write(writebuffer);
                        system.out.println("向服务端发送数据结束");
                    }
                }
            }
        }

        /**
         * 服务器连接成功
         * 向服务端发送数据结束
         */

    }
}

三、aio(asynchronous io) 即nio2.0

异步非阻塞,由操作系统完成后回调通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。

应用场景:aio方式适用于连接数目多且连接时间较长(重操作)的架构(应用),jdk7开始支持。

著名的异步网络通讯框架netty之所以废弃了aio,原因是:在linux系统上,nio的底层实现使用了epoll,而aio的底层实现仍使用epoll,没有很好实现aio,因此在性能上没有明显的优势,而且被jdk封装了一层不容易深度优 化,linux上aio还不够成熟

aio示例代码如下:

服务端

import java.net.inetsocketaddress;
import java.nio.bytebuffer;
import java.nio.channels.asynchronousserversocketchannel;
import java.nio.channels.asynchronoussocketchannel;
import java.nio.channels.completionhandler;

/**
 * @title:aio服务端
 * @author:wangchenggong
 * @date 2021/4/14 17:05
 * @description
 * @version
 */
public class aioserver {

    public static void main(string[] args) throws exception {
        final asynchronousserversocketchannel serverchannel = asynchronousserversocketchannel.open().bind(new inetsocketaddress(9000));
        serverchannel.accept(null, new completionhandler<asynchronoussocketchannel, object>() {
            @override
            public void completed(asynchronoussocketchannel socketchannel, object attachment) {
                try{
                    system.out.println("2--"+thread.currentthread().getname());
                    //接收客户端连接
                    serverchannel.accept(attachment,this);
                    system.out.println("客户端"+socketchannel.getremoteaddress()+"已连接");

                    bytebuffer buffer = bytebuffer.allocate(128);
                    socketchannel.read(buffer, null, new completionhandler<integer, object>() {
                        @override
                        public void completed(integer result, object attachment) {
                            system.out.println("3--"+thread.currentthread().getname());
                            //flip方法将buffer从写模式切换到读模式
                            //如果没有,就是从文件最后开始读取的,当然读出来的都是byte=0时候的字符。通过buffer.flip();这个语句,就能把buffer的当前位置更改为buffer缓冲区的第一个位置
                            buffer.flip();
                            system.out.println(new string(buffer.array(), 0, result));
                            socketchannel.write(bytebuffer.wrap("hello aio client!".getbytes()));
                        }

                        @override
                        public void failed(throwable exc, object attachment) {
                            exc.printstacktrace();
                        }
                    });

                }catch(exception e){
                    e.printstacktrace();
                }
            }

            @override
            public void failed(throwable exc, object attachment) {

            }
        });

        system.out.println("1‐‐main"+thread.currentthread().getname());
        thread.sleep(integer.max_value);
    }
    /**
     * 1‐‐mainmain
     * 2--thread-9
     * 客户端/127.0.0.1:54821已连接
     * 3--thread-8
     * hello aio server !
     * 2--thread-9
     * 客户端/127.0.0.1:54942已连接
     * 3--thread-7
     * hello aio server !
     */

}

客户端

import java.net.inetsocketaddress;
import java.nio.bytebuffer;
import java.nio.channels.asynchronoussocketchannel;

/**
 * @title:aio客户端
 * @author:wangchenggong
 * @date 2021/4/14 16:56
 * @description
 * @version
 */
public class aioclient {

    public static void main(string[] args) throws exception {

        //创建aio客户端
        asynchronoussocketchannel socketchannel = asynchronoussocketchannel.open();
        socketchannel.connect(new inetsocketaddress("localhost", 9000)).get();
        //发送消息
        socketchannel.write(bytebuffer.wrap("hello aio server !".getbytes()));
        //接收消息
        bytebuffer buffer = bytebuffer.allocate(128);
        integer len = socketchannel.read(buffer).get();
        if(len != -1){
            //客户端收到消息:hello aio client!
            system.out.println("客户端收到消息:"+new string(buffer.array(), 0, len));
        }
    }


}

四、总结

bio   nio aio
io模型    同步阻塞 同步非阻塞 异步非阻塞
编程难度    简单 复杂 复杂
可靠性 好
吞吐量   低 

到此这篇关于java中bio、nio、aio都有啥区别的文章就介绍到这了,更多相关java中bio、nio、aio的区别内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!