我的NIO学习笔记

一、文章来由

研究Nio也有几天了,在网上看了很多文章,给人整体的感觉就是,一个原本简简单单的东西,被说的好复杂。或者是类似 http://ifeve.com/selectors/ 这种百科全书式的教你如何用接口,这种文章看似介绍了每个函数,面面俱到,却很难串起来。

但是人学东西本来就是一个感性认识到理性认识的过程,如果看到的都是单点,怎么在脑海中形成一张图?

(1)是什么(功能)

(2)为什么这么设计(应用场景)

(3)怎么实现的(具体代码)

这三个问号搞完了以后,才是如何去使用,不能本末倒置。

另外最近实习,需要学习很多东西,文章大都是网上质量高一些的文章的转载,原创文章就相对少一些~~

二、NIO设计原理

一个IO操作分成了两个步骤:①发起IO请求实际的IO操作

(1)阻塞IO和非阻塞IO的区别在于第一步,发起IO请求是否会被阻塞,如果阻塞直到完成那么就是传统的阻塞IO,如果不阻塞,那么就是非阻塞IO。

(2)同步IO和异步IO的区别就在于第二个步骤是否阻塞

传统的 IO 称为 BIO,是一种同步阻塞 IO 。要知道 IO 都是交给操作系统完成的,系统在 IO 的时候,如果当前线程等待在那里,效率是很低的。在网络编程里,传统的socket也是 BIO,一个连接一个线程。

于是 NIO 出现了,它是一种同步非阻塞 IO,其实说白了,就是用一个线程不断轮询,去看有没有事件去处理…

服务员不断询问顾客有没有什么要求,然后同步去处理~~

用户自己实现的一个while循环,while(selector.select() > 0),通过select阻塞的轮询,来看是否有事件处理。如果有处理就完了。

道理就这么简单。

三、selector

selector就是那个轮询的处理器,它采用register的模式,要被selector轮询,一定要登记。就像你去餐馆吃东西,一定要点菜一样。

selector可以监听四种不同类型的事件:

Connect

Accept

Read

Write

这四种事件用SelectionKey的四个常量来表示:

SelectionKey.OP_CONNECT

SelectionKey.OP_ACCEPT

SelectionKey.OP_READ

SelectionKey.OP_WRITE

对应成代码就是:

Selector selector = Selector.open();
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
while(true) {
  int readyChannels = selector.select();
  if(readyChannels == 0) continue;
  Set selectedKeys = selector.selectedKeys();
  Iterator keyIterator = selectedKeys.iterator();
  while(keyIterator.hasNext()) {
    SelectionKey key = keyIterator.next();
    if(key.isAcceptable()) {
        // a connection was accepted by a ServerSocketChannel.
    } else if (key.isConnectable()) {
        // a connection was established with a remote server.
    } else if (key.isReadable()) {
        // a channel is ready for reading
    } else if (key.isWritable()) {
        // a channel is ready for writing
    }
    keyIterator.remove();
  }
}

很明显的是:

但是客户端注册selector的时候,不可能有serverChannel的出现了,所以客户端不可能有isAcceptable事件,就像服务端不可能有isConnectable一样

当连接建立后两段都需要获得socketchannel来通信 ,跟socket一样,事实上,如果不用selector的话,跟socket就是一毛一样的,socketChannel底层也就是socket。如下:

/////NioServer.java
package 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.nio.charset.Charset;

/**
 * Created by hupo.wh on 2016/7/16.
 */
public class NioServer {

    public static void main(String args[]) throws IOException {

        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        serverSocketChannel.socket().bind(new InetSocketAddress(8888));
        serverSocketChannel.configureBlocking(false); //非阻塞

        //字符序列和字节序列的编码和解码
        Charset charset = Charset.forName("UTF-8");

        ByteBuffer buf = ByteBuffer.allocate(48);
        while(true){
            SocketChannel socketChannel =
                    serverSocketChannel.accept();

            String mesg = "";
            if(socketChannel != null){
                //do something with socketChannel...
                System.out.println("Connection has been built!!!");

                while (socketChannel.read(buf) > 0) {
                    buf.flip();
                    mesg += charset.decode(buf);
                }
                System.out.println(mesg);

            }
        }
    }
}
/////NioClient.java
package nio;

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

/**
 * Created by hupo.wh on 2016/7/15.
 */
public class NioClient {

    public static void main(String args[]) throws IOException {

        SocketChannel sc = SocketChannel.open();
        sc.connect(new InetSocketAddress("127.0.0.1",8888));
        sc.configureBlocking(false);

        String newData = "Hello Wanghu..." + System.currentTimeMillis();

        ByteBuffer buf = ByteBuffer.allocate(48);
        buf.clear();
        buf.put(newData.getBytes());

        buf.flip();

        while(buf.hasRemaining()) {
            sc.write(buf);
        }

        sc.close();
    }
}

四、例程

server例程一:

package nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * Created by hupo.wh on 2016/7/16.
 */
public class App2 {

    public static void main(String[] args) throws IOException {

        int port = 30;
        ServerSocketChannel serverChannel = ServerSocketChannel.open( );
        ServerSocket serverSocket = serverChannel.socket( );
        Selector selector = Selector.open( );
        serverSocket.bind (new InetSocketAddress(port));
        serverChannel.configureBlocking (false);
        serverChannel.register (selector, SelectionKey.OP_ACCEPT);

        while (true) {

            int n = selector.select( );
            if (n == 0) {
                continue; // nothing to do
            }

            Iterator it = selector.selectedKeys().iterator( );

            while (it.hasNext( )) {

                SelectionKey key = (SelectionKey) it.next( );

                if (key.isAcceptable( )) {
                    ServerSocketChannel server =
                            (ServerSocketChannel) key.channel( );
                    SocketChannel channel = server.accept( );
                    if (channel == null) {
                        ;//handle code, could happen
                    }
                    channel.configureBlocking (false);
                    channel.register (selector, SelectionKey.OP_READ);

                }

                if (key.isReadable( )) {
                    //readDataFromSocket (key);
                }
                it.remove( );
            }
        }
    }
}

Server例程二:

package nio;

/**
 * Created by hupo.wh on 2016/7/16.
 */
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.spi.SelectorProvider;
import java.nio.charset.Charset;
import java.util.Set;

/**
 * Created by wwh on 15-7-25.
 */
public class NioSocket {
    //字符序列和字节序列的编码和解码
    private Charset charset = Charset.forName("UTF-8");

    void run(String ip, int port) throws IOException {
        try {
            //创建服务端套接字
            ServerSocketChannel server = ServerSocketChannel.open();
            //绑定ip和端口
            server.socket().bind(new InetSocketAddress(ip, port));
            //设置非阻塞
            server.configureBlocking(false);
            //创建selector事件选择器
            Selector selector = Selector.open();
            //将自己的监听套接字注册到selector上,监听 accept事件
            //SelectionKey代表SelectableChannel和Selector的关系,Selectable是Selector可监听的事件channel.
            server.register(selector, SelectionKey.OP_ACCEPT);
            while(selector.select() > 0){
                //selector.select()返回事件
                for(SelectionKey sk : selector.selectedKeys()) {
                    //从事件集合中删除正要处理的事件
                    selector.selectedKeys().remove(sk);
                    //判断事件的类型,依次处理
                    if(sk.isAcceptable()){
                        //如果事件为接受连接accpet事件
                        System.out.println("accpet 事件");
                        //调用accept接受请求连接
                        SocketChannel client = server.accept();
                        //设置为非阻塞
                        client.configureBlocking(false);
                        //向selector上注册读事件
                        client.register(selector, SelectionKey.OP_READ);
                        //将sk对应的channel设置为准备接受其他请求
                        sk.interestOps(SelectionKey.OP_ACCEPT);
                    }

                    if(sk.isReadable()){
                        //如果事件为可读事件
                        System.out.println("read 事件");
                        SocketChannel client = (SocketChannel)sk.channel();
                        //定义缓冲区
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        String mesg = "";

                        try {
                            while (client.read(buffer) > 0) {
                                buffer.flip();
                                mesg += charset.decode(buffer);
                            }
                            System.out.println("收到:" + mesg);
                            sk.interestOps(SelectionKey.OP_READ);
                        }catch (IOException e){
                            //如果出现异常,则取消当前的client连接
                            sk.cancel();
                            if(sk.channel() != null){
                                sk.channel().close();
                            }
                        }

                        //回复给发来消息的client
                        client.write(charset.encode(mesg));
                        System.out.println("回复:" + mesg);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        NioSocket Server = new NioSocket();
        Server.run("localhost", 10000);
    }
}
时间: 2024-10-25 14:26:02

我的NIO学习笔记的相关文章

java nio学习笔记(一)

位置保留,待用 java nio学习笔记(一),布布扣,bubuko.com

NIO学习笔记1

NIO引入了三个概念: Buffer 缓冲区 Channel 通道 selector 选择器 1.java.io优化建议 操作系统与Java基于流的I/O模型有些不匹配.操作系统要移动的是大块数据(缓冲区),这往往是在硬件直接存储器存取(DMA)的协助下完成的.I/O类喜欢操作小块数据——单个字节.几行文本.结果,操作系统送来整缓冲区的数据,java.io的流数据类再花大量时间把它们拆成小块,往往拷贝一个小块就要往返于几层对象.操作系统喜欢整卡车地运来数据,java.io类则喜欢一铲子一铲子地加

java NIO 学习笔记(一)

相关概念:缓冲区和通道 解释:标准的IO流是基于字节流和字符流的而NIO是基于通道和缓冲区的,数据总是从通道读取到缓冲区或者从缓冲区读取到通道的. 相关概念:非阻塞IO 解释:当线程从通道读取数据到缓冲区时线程同事还可以进行其他的事情. 相关概念:selectors(选择器) 解释:单个线程可以监听多个通信通道.

Java NIO学习笔记(一)

文章目录: 1.什么是IO 2.什么是Java NIO 3.I/O常见概念 4.为什么使用NIO 5.IO VS NIO 一.什么是IO I/O 或者输入/输出 , 指的是计算机与外部世界或者一个程序与计算机的其余部分的之间的接口.它对于任何计算机系统都非常关键,因而所有 I/O 的主体实际上是内置在操作系统中的.单独的程序一般是让系统为它们完成大部分的工作.在 Java 编程中,直到最近一直使用 流 的方式完成 I/O.所有 I/O 都被视为单个的字节的移动,通过一个称为 Stream 的对象

java NIO 学习笔记

项目组是做IM产品的,服务端当然用的是NIO技术做通信底层.但是一直都是对NIO有些理论的了解,没有实践,最近有空了,就实践了下NIO. NIO,新IO,也称之为非阻塞IO.非阻塞是它跟传统IO的最重要的区别之一.传统IO用Socket进行通信,NIO则用channel进行消息交互.channel必须注册到selector上,把它感兴趣的事件告诉selector.这是个观察者模式的实现.可以这样描述channel和selector的关系,channel是火车轨道,selector是火车调度室.多

Java NIO学习笔记

NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作上,过去,在打开一个I/O通道后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,read()也是傻傻的等,这会影响我们程序继续做其他事情,那么改进做法就是开设线程,让线程去等待,但是这样做也是相当耗费资源的. Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启

Java NIO学习笔记八 DatagramChannel

Java NIO DatagramChannel Java NIO DatagramChannel是可以发送和接收UDP数据包的通道.由于UDP是一种无连接网络协议,因此您不能默认读取和写入DatagramChannel其他通道.而是发送和接收数据包. 打开DatagramChannel 打开一个DatagramChannel代码: DatagramChannel channel = DatagramChannel.open(); channel.socket().bind(new InetSo

Java NIO学习笔记七 Non-blocking Server

Java NIO:Non-blocking Server 即使你了解了Java NIO非阻塞功能的工作(怎么样Selector,Channel, Buffer等等),设计一个无阻塞服务器仍然很难.非阻塞IO包含了相比阻塞IO的要有难度.本章非阻塞服务器教程将讨论非阻塞服务器的主要挑战,并为他们描述一些潜在的解决方案. 本教程中描述的想法是围绕Java NIO设计的.但是,我认为,只要有这样的构造,这些想法可以用其他语言重复使用Selector.据我所知,这样的结构是由底层操作系统提供的,所以很有

nio学习笔记

Java NIO(New IO或 Non Blocking IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API.NIO支持面向缓冲区的.基于通道的IO操作.NIO将以更加高效的方式进行文件的读写操作. java IO 与 java NIO 的区别  一.通道(Channel)与缓冲区(Buffer) 若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区.然后操作缓冲区,对数据进行处理.简而言之,Channel 负责传