什么是bio

同步阻塞式IO,服务端创立一个ServerSocket,而后客户端用一个Socket去连贯那个ServerSocket,而后ServerSocket接管到一个Socket的连贯申请就创立一个Socket和一个线程去跟那个Socket进行通信。

public class BioServer {      public static void main(String[] args) {        // 服务端开启一个端口进行监听        int port = 8080;        ServerSocket serverSocket = null;   //服务端        Socket socket;  //客户端        InputStream in = null;        OutputStream out = null;        try {            serverSocket = new ServerSocket(port);  //通过构造函数创立ServerSocket,指定监听端口,如果端口非法且闲暇,服务器就会监听胜利            // 通过有限循环监听客户端连贯,如果没有客户端接入,则会阻塞在accept操作            while (true) {                System.out.println("Waiting for a new Socket to establish" + " ," + new Date().toString());                socket = serverSocket.accept();//阻塞  三次握手                in = socket.getInputStream();                byte[] buffer = new byte[1024];                int length = 0;                while ((length = in.read(buffer)) > 0) {//阻塞                    System.out.println("input is:" + new String(buffer, 0, length) + " ," + new Date().toString());                    out = socket.getOutputStream();                    out.write("success".getBytes());                    System.out.println("Server end" + " ," + new Date().toString());                }            }        } catch (Exception e) {            e.printStackTrace();        } finally {            // 必要的清理流动            if (serverSocket != null) {                try {                    serverSocket.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (in != null) {                try {                    in.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (out != null) {                try {                    out.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }}

什么是nio

同步非阻塞
包含Selector,这是多路复用器,selector会一直轮询注册的channel,如果某个channel上产生了读写事件,selector就会将这些channel获取进去,咱们通过SelectionKey获取有读写事件的channel,就能够进行IO操作。一个Selector就通过一个线程,就能够轮询成千上万的channel,这就意味着你的服务端能够接入成千上万的客户端。

public class NioDemo implements Runnable {    public int id = 100001;    public int bufferSize = 2048;    @Override    public void run() {        init();    }    public void init() {        try {            // 创立通道和选择器            ServerSocketChannel socketChannel = ServerSocketChannel.open();            Selector selector = Selector.open();            InetSocketAddress inetSocketAddress = new InetSocketAddress(                    InetAddress.getLocalHost(), 4700);            socketChannel.socket().bind(inetSocketAddress);            // 设置通道非阻塞 绑定选择器            socketChannel.configureBlocking(false);            socketChannel.register(selector, SelectionKey.OP_ACCEPT).attach(                    id++);            System.out.println("Server started .... port:4700");            listener(selector);        } catch (Exception e) {        }    }    public void listener(Selector in_selector) {        try {            while (true) {                Thread.sleep(1 * 1000);                in_selector.select(); // 阻塞 直到有就绪事件为止                Set<SelectionKey> readySelectionKey = in_selector                        .selectedKeys();                Iterator<SelectionKey> it = readySelectionKey.iterator();                while (it.hasNext()) {                    SelectionKey selectionKey = it.next();                    // 判断是哪个事件                    if (selectionKey.isAcceptable()) {// 客户申请连贯                        System.out.println(selectionKey.attachment()                                + " - 承受申请事件");                        // 获取通道 承受连贯,                        // 设置非阻塞模式(必须),同时须要注册 读写数据的事件,这样有音讯触发时能力捕捉                        ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey                                .channel();                        serverSocketChannel                                .accept()                                .configureBlocking(false)                                .register(                                        in_selector,                                        SelectionKey.OP_READ                                                | SelectionKey.OP_WRITE).attach(id++);                        System.out                                .println(selectionKey.attachment() + " - 已连贯");                        // 上面这种写法是有问题的 不应该在serverSocketChannel下面注册                        /*                         * serverSocketChannel.configureBlocking(false);                         * serverSocketChannel.register(in_selector,                         * SelectionKey.OP_READ);                         * serverSocketChannel.register(in_selector,                         * SelectionKey.OP_WRITE);                         */                    }                    if (selectionKey.isReadable()) {// 读数据                        System.out.println(selectionKey.attachment()                                + " - 读数据事件");                        SocketChannel clientChannel = (SocketChannel) selectionKey.channel();                        ByteBuffer receiveBuf = ByteBuffer.allocate(bufferSize);                        clientChannel.read(receiveBuf);                        System.out.println(selectionKey.attachment()                                + " - 读取数据:" + getString(receiveBuf));                    }                    if (selectionKey.isWritable()) {// 写数据                        System.out.println(selectionKey.attachment()                                + " - 写数据事件");                        SocketChannel clientChannel = (SocketChannel) selectionKey.channel();                        ByteBuffer sendBuf = ByteBuffer.allocate(bufferSize);                        String sendText = "hello\n";                        sendBuf.put(sendText.getBytes());                        sendBuf.flip();        //写完数据后调用此办法                        clientChannel.write(sendBuf);                    }                    if (selectionKey.isConnectable()) {                        System.out.println(selectionKey.attachment()                                + " - 连贯事件");                    }                    // 必须removed 否则会持续存在,下一次循环还会进来,                    // 留神removed 的地位,针对一个.next() remove一次                    it.remove();                }            }        } catch (Exception e) {            System.out.println("Error - " + e.getMessage());            e.printStackTrace();        }    }    /**     * ByteBuffer 转换 String     *     * @param buffer     * @return     */    public static String getString(ByteBuffer buffer) {        String string = "";        try {            for (int i = 0; i < buffer.position(); i++) {                string += (char) buffer.get(i);            }            return string;        } catch (Exception ex) {            ex.printStackTrace();            return "";        }    }}

什么是aio

异步非阻塞
每个连贯发送过去的申请,都会绑定一个buffer,而后告诉操作系统去异步实现读,此时你的程序是会去干别的事儿的,等操作系统实现数据读取之后,就会回调你的接口,给你操作系统异步读完的数据。

public class AIOServer {    public final static int PORT = 9888;    private AsynchronousServerSocketChannel server;    public AIOServer() throws IOException {        server = AsynchronousServerSocketChannel.open().bind(                new InetSocketAddress(PORT));    }    public void startWithFuture() throws InterruptedException,            ExecutionException, TimeoutException {        while (true) {// 循环接管客户端申请            Future<AsynchronousSocketChannel> future = server.accept();            AsynchronousSocketChannel socket = future.get();// get() 是为了确保 accept 到一个连贯            handleWithFuture(socket);        }    }    public void handleWithFuture(AsynchronousSocketChannel channel) throws InterruptedException, ExecutionException, TimeoutException {        ByteBuffer readBuf = ByteBuffer.allocate(2);        readBuf.clear();        while (true) {// 一次可能读不完            //get 是为了确保 read 实现,超时工夫能够无效防止DOS攻打,如果客户端始终不发送数据,则进行超时解决            Integer integer = channel.read(readBuf).get(10, TimeUnit.SECONDS);            System.out.println("read: " + integer);            if (integer == -1) {                break;            }            readBuf.flip();            System.out.println("received: " + Charset.forName("UTF-8").decode(readBuf));            readBuf.clear();        }    }    public void startWithCompletionHandler() throws InterruptedException,            ExecutionException, TimeoutException {        server.accept(null,                new CompletionHandler<AsynchronousSocketChannel, Object>() {                    public void completed(AsynchronousSocketChannel result, Object attachment) {                        server.accept(null, this);// 再此接管客户端连贯                        handleWithCompletionHandler(result);                    }                    @Override                    public void failed(Throwable exc, Object attachment) {                        exc.printStackTrace();                    }                });    }    public void handleWithCompletionHandler(final AsynchronousSocketChannel channel) {        try {            final ByteBuffer buffer = ByteBuffer.allocate(4);            final long timeout = 10L;            channel.read(buffer, timeout, TimeUnit.SECONDS, null, new CompletionHandler<Integer, Object>() {                @Override                public void completed(Integer result, Object attachment) {                    System.out.println("read:" + result);                    if (result == -1) {                        try {                            channel.close();                        } catch (IOException e) {                            e.printStackTrace();                        }                        return;                    }                    buffer.flip();                    System.out.println("received message:" + Charset.forName("UTF-8").decode(buffer));                    buffer.clear();                    channel.read(buffer, timeout, TimeUnit.SECONDS, null, this);                }                @Override                public void failed(Throwable exc, Object attachment) {                    exc.printStackTrace();                }            });        } catch (Exception e) {            e.printStackTrace();        }    }    public static void main(String args[]) throws Exception {//        new AIOServer().startWithFuture();        new AIOServer().startWithCompletionHandler();        Thread.sleep(100000);    }}

什么是epoll

一种多路复用的技术,能够解决之前poll和select大量并发连贯状况下cpu利用率过高,以及须要遍历整个被侦听的描述符集的问题。epoll只有遍历那些被内核IO事件异步唤醒而退出Ready队列的描述符汇合就行了。

什么是mmap技术

把一个磁盘文件映射到内存里来,而后把映射到内存里来的数据通过socket发送进来
有一种mmap技术,也就是内存映射,间接将磁盘文件数据映射到内核缓冲区,这个映射的过程是基于DMA引擎拷贝的,同时用户缓 冲区是跟内核缓冲区共享一块映射数据的,建设共享映射之后,就不须要从内核缓冲区拷贝到用户缓冲区了
光是这一点,就能够防止一次拷贝了,然而这个过程中还是会用户态切换到内核态去进行映射拷贝,接着再次从内核态切换到用户态, 建设用户缓冲区和内核缓冲区的映射
接着把数据通过Socket发送进来,还是要再次切换到内核态
接着间接把内核缓冲区里的数据拷贝到Socket缓冲区里去,而后再拷贝到网络协议引擎里,发送进来就能够了,最初切换回用户态
缩小一次拷贝,然而并不缩小切换次数,一共是4次切换,3次拷贝

什么是零拷贝技术

linux提供了sendfile,也就是零拷贝技术
这个零拷贝技术,就是先从用户态切换到内核态,在内核态的状态下,把磁盘上的数据拷贝到内核缓冲区,同时从内核缓冲区拷贝一些 offset和length到Socket缓冲区;接着从内核态切换到用户态,从内核缓冲区间接把数据拷贝到网络协议引擎里去
同时从Socket缓冲区里拷贝一些offset和length到网络协议引擎里去,然而这个offset和length的量很少,简直能够疏忽
只有2次切换,2次拷贝,就能够了

说一下select,poll,epoll的区别?

selectpoll实现须要本人一直轮询所有fd汇合,直到设施就绪,期间可能要睡眠和唤醒屡次交替。
epoll也须要调用epoll_wait一直轮询就绪链表,期间也可能屡次睡眠和唤醒交替,然而它是设施就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的过程。尽管都要睡眠和交替,然而select和poll在“醒着”的时候要遍历整个fd汇合,而epoll在“醒着”的时候只有判断一下就绪链表是否为空就行了,这节俭了大量的CPU工夫。这就是回调机制带来的性能晋升。

selectpoll每次调用都要把fd汇合从用户态往内核态拷贝一次,并且要把current往设施期待队列中挂一次,而epoll只有一次拷贝,而且把current往期待队列上挂也只挂一次(在epoll_wait的开始,留神这里的期待队列并不是设施期待队列,只是一个epoll外部定义的期待队列)。这也能节俭不少的开销。