首页 > 其他 > 详细

我的NIO学习笔记

时间:2016-07-16 23:36:15      阅读:297      评论:0      收藏:0      [点我收藏+]

一、文章来由

研究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);
    }
}

我的NIO学习笔记

原文:http://blog.csdn.net/scythe666/article/details/51925016

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!