标签:
研究Nio也有几天了,在网上看了很多文章,给人整体的感觉就是,一个原本简简单单的东西,被说的好复杂。或者是类似 http://ifeve.com/selectors/ 这种百科全书式的教你如何用接口,这种文章看似介绍了每个函数,面面俱到,却很难串起来。
但是人学东西本来就是一个感性认识到理性认识的过程,如果看到的都是单点,怎么在脑海中形成一张图?
(1)是什么(功能) 
(2)为什么这么设计(应用场景) 
(3)怎么实现的(具体代码)
这三个问号搞完了以后,才是如何去使用,不能本末倒置。
另外最近实习,需要学习很多东西,文章大都是网上质量高一些的文章的转载,原创文章就相对少一些~~
一个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就是那个轮询的处理器,它采用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);
    }
}
标签:
原文地址:http://blog.csdn.net/scythe666/article/details/51925016