NIO--Selector

150 阅读1分钟
import java.io.IOException;
import java.nio.channels.SelectionKey;

public interface TCPProtocol {
    void handleAccept(SelectionKey key) throws IOException;
    void handleRead(SelectionKey key) throws IOException;
    void handleWrite(SelectionKey key) throws IOException;
}
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class EchoSelectorProtocol implements TCPProtocol {
    private int bufSize;
    public EchoSelectorProtocol(int bufSize) {
        this.bufSize = bufSize;
    }

    /**
     * 获取与该客户端的信道 并交给 selector 管理
     * @param key
     * @throws IOException
     */
    @Override
    public void handleAccept(SelectionKey key) throws IOException {
        SocketChannel clnChannel = ((ServerSocketChannel) key.channel()).accept();
        clnChannel.configureBlocking(false);
        //TODO 这里为什么需要一个 Buffer
        //buffer 将作为附件,与register()方法所返回的 SelectorKey 实例相关联。为了之后数据的存取
        clnChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufSize));
    }

    @Override
    public void handleRead(SelectionKey key) throws IOException {
        //支持数据读取 是个 SocketChannel
        SocketChannel clnChannel = (SocketChannel) key.channel();
        //获取相关联的缓冲区(连接建立后,有一个ByteBuffer附加在该 SelectorKey 实例上)
        ByteBuffer buf = (ByteBuffer) key.attachment();
        long byteRead = clnChannel.read(buf);
        if(byteRead == -1){
            clnChannel.close();
        }else if(byteRead > 0){
            key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
        }
    }

    @Override
    public void handleWrite(SelectionKey key) throws IOException {
        ByteBuffer buf = (ByteBuffer) key.attachment();
        buf.flip(); // prepare for writing 使写数据的操作开始消耗由读操作产生的数据
        SocketChannel clnChan = (SocketChannel) key.channel();
        clnChan.write(buf);
        //如果缓冲区中之前接收的数据已经没有剩余,则修改该键关联的操作集,指示其只能进行读操作
        if(!buf.hasRemaining()){
            key.interestOps(SelectionKey.OP_READ);
        }
        //压缩缓冲区:如果缓冲区中还有剩余数据,该操作则将其移动到缓冲区的前端
        //以使下次迭代能够读入更多数据
        buf.compact(); // make room for more data to be read in
    }
}
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;

/**
 * 服务端选择器
 */
public class TCPServerSelector {
    static final int BUFSIZE = 256;
    static final int TIMEOUT = 3000; //milliseconds

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

        //selector 管理 listenChannel 和 所有 clnChannel
        Selector selector = Selector.open();

        ServerSocketChannel listenChannel = ServerSocketChannel.open();
        listenChannel.socket().bind(new InetSocketAddress("127.0.0.1", 9999));
        listenChannel.configureBlocking(false);
        listenChannel.register(selector, SelectionKey.OP_ACCEPT);

        //create a handler that will implement the protocol
        TCPProtocol protocol = new EchoSelectorProtocol(BUFSIZE);

        //run forever, processing available IO operations
        while(true){
            //wait for some channel to be ready(or timeout)
            if(selector.select(TIMEOUT) == 0){
                System.out.print(".");
                continue;
            }

            Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
            while(keyIter.hasNext()){
                SelectionKey key = keyIter.next();
                if(key.isAcceptable()){
                    protocol.handleAccept(key);
                }
                if(key.isReadable()){
                    protocol.handleRead(key);
                }
                if(key.isValid() && key.isWritable()){
                    protocol.handleWrite(key);
                }
                keyIter.remove();
            }
        }
    }
}