package lyx.demo.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
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 landyChris on 2017/11/3.
 * 使用telnet localhost 8888进行测试
 */
public class NioSocketDemo {

    private Selector selector;//通道选择器

    public static void main(String[] args) throws IOException {
        NioSocketDemo nioSocketDemo = new NioSocketDemo();
        nioSocketDemo.initServer(8888);
        nioSocketDemo.listenSelector();
    }

    public void initServer(int port) throws IOException {
        //通道
        ServerSocketChannel ssc = ServerSocketChannel.open();

        ssc.configureBlocking(false);//设置成非阻塞

        //绑定端口
        ssc.socket().bind(new InetSocketAddress(port));

        //获取到选择器
        selector = Selector.open();

        //注册到选择器上
        //监听用户的连接事件
        ssc.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("服务已经启动.......");
    }

    public void listenSelector() throws IOException{
        //轮询监听Selector
        while(true) {
            //最终会调用到操作系统中的多用复用选择器(本地方法)方法(如poll/epoll/select等)
            //等待客户连接
            this.selector.select();
            //迭代SelectionKey中的键值
            Iterator<SelectionKey> it = this.selector.selectedKeys().iterator();

            while(it.hasNext()) {
                SelectionKey key = it.next();
                //删除当前SelectionKey
                it.remove();
                //处理请求
                handler(key);
            }
        }
    }

    private void handler(SelectionKey key) throws IOException{
        if(key.isAcceptable()) { //连接事件
            System.out.println("新客户端连接上了....");
            //处理客户端请求事件
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            SocketChannel sc = ssc.accept();
            sc.configureBlocking(false);//设置成非阻塞
            //接收客户端发送的信息时,需要给通道设置读的权限
            sc.register(selector,SelectionKey.OP_READ);
        }else if(key.isReadable()) {
            SocketChannel sc = (SocketChannel)key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int readData = sc.read(buffer);
            if(readData > 0) {
                String info = new String(buffer.array(),"GBK");
                System.out.println("服务端接收到数据:" + info);
            }else {
                System.out.println("客户端关闭了....");
                key.cancel();
            }
        }
    }

}