我的NIO学习笔记

来源:互联网 发布:三明治板的刚度矩阵 编辑:程序博客网 时间:2024/05/22 04:54

一、文章来由

研究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.javapackage 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.javapackage 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);    }}
2 0