package example;
/**
 *  端口转发
 */
//import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
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.HashMap;
import java.util.Iterator;
public class myServer {
    public static final int PORTClient = 8085;
    public static final int PORTDevice = 8081;
    protected static HashMap<String,SelectionKey> map = new HashMap<String, SelectionKey>();
    //定义选择器
    protected Selector selector;
    protected Selector selector_device;
    int clientCount;
    int deviceCount;
    public myServer() throws Exception {
            //外部访问的socket和selector
            selector = Selector.open();
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(PORTClient)); // port
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// register         
            p("Server localhost:" + PORTClient + " started. waiting for clients. ");
              
            //内部设备端口
            selector_device=Selector.open();
            ServerSocketChannel serverSocketChannelDevice = ServerSocketChannel.open();
            serverSocketChannelDevice.socket().bind(new InetSocketAddress(PORTDevice));
            serverSocketChannelDevice.configureBlocking(false);
            serverSocketChannelDevice.register(selector_device, SelectionKey.OP_ACCEPT);
            p("Server localhost:" + PORTDevice + " started. waiting for Device. ");
            Client client=new Client();
            Device device=new Device();
            device.run();
            while (true) {
                  
                  
                client.run();
                      
            }
        }
    class Client implements Runnable{
        @Override
        public void run() {
            // TODO Auto-generated method stub
            try {
                // selector 线程。select() 会阻塞,直到有客户端连接,或者有消息读入
                selector.select();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }                  
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                // hashmap.put(, selectionKey);
                iterator.remove(); // 删除此消息
                // 并在当前线程内处理。(为了高效,一般会在另一个线程中处理此消息,例如使用线程池等)
                try {
                    handleSelectionKey(selectionKey);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        public void start() {
            // TODO Auto-generated method stub
              
        }
          
    }
    class Device implements Runnable
    {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            //
            try {
                selector_device.select();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Iterator<SelectionKey> iteratorDevice = selector_device.selectedKeys().iterator();
            while (iteratorDevice.hasNext()) {
                SelectionKey selectionKeyDevice = iteratorDevice.next();
                System.out.println(selectionKeyDevice);
                // hashmap.put(, selectionKey);
                iteratorDevice.remove(); // 删除此消息
                // 并在当前线程内处理。(为了高效,一般会在另一个线程中处理此消息,例如使用线程池等)
                try {
                    handleDevice(selectionKeyDevice);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        public void start() {
            // TODO Auto-generated method stub
              
        }
          
    }
    //内部设备连接
    private void handleDevice(SelectionKey selectionKeyDevice) throws IOException {
        // TODO Auto-generated method stub
        if (selectionKeyDevice.isAcceptable()) {
            // 有客户端进来
            deviceCount++;
            System.out.println(deviceCount);
            ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKeyDevice.channel();
            SocketChannel socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            Socket socket = socketChannel.socket();
            // 立即注册一个 OP_READ 的SelectionKey, 接收客户端的消息
            SelectionKey key = socketChannel.register(selector_device, SelectionKey.OP_READ);
            System.out.println(key);
            String str=socket.getRemoteSocketAddress().toString();
            String [] strs = str.split("[:]");
            //做内部设备的访问map
            map.put(strs[0], key);
            System.out.println(map.get(socket.getRemoteSocketAddress()));
            System.out.println(socket.getInetAddress().getHostAddress());
            key.attach("第 " + deviceCount + " 个内部设备 [" + socket.getRemoteSocketAddress() + "]: ");
            p(key.p_w_upload() + "\t[connected] =========================================");
            } else if (selectionKeyDevice.isReadable()) {
                // 有消息进来
                ByteBuffer byteBufferD = ByteBuffer.allocate(100);
                SocketChannel socketChannel = (SocketChannel) selectionKeyDevice.channel();
                try {
                    int len = socketChannel.read(byteBufferD);
                    if (len > 0) {
                        byteBufferD.flip();//flip()方法用来将缓冲区准备为数据传出状态,这通过将limit设置为position的当前值,再 将position的值设为0来实现
                        socketChannel.write(byteBufferD);
                        //socketChannelDevice.write(byteBuffer);
   
                    } else {
                        // 输入结束,关闭 socketChannel
                        p(selectionKeyDevice.p_w_upload() + "read finished. close socketChannel. ");
                        socketChannel.close();
                    }
                } catch (Exception e) {
                    // 如果read抛出异常,表示连接异常中断,需要关闭 socketChannel
                    e.printStackTrace();
                    p(selectionKeyDevice.p_w_upload() + "socket closed? ");
                    socketChannel.close();
                }
            } else if (selectionKeyDevice.isWritable()) {
                p(selectionKeyDevice.p_w_upload() + "TODO: isWritable() ???????????????????????????? ");
            } else if (selectionKeyDevice.isConnectable()) {
                p(selectionKeyDevice.p_w_upload() + "TODO: isConnectable() ????????????????????????? ");
            } else {
                p(selectionKeyDevice.p_w_upload() + "TODO: else. ");
            }
    }
    //外部用户访问
    public void handleSelectionKey(SelectionKey selectionKey) throws Exception {
            if (selectionKey.isAcceptable()) {
                // 有客户端进来
                clientCount++;
                ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
                SocketChannel socketChannel = serverSocketChannel.accept();
                socketChannel.configureBlocking(false);
                Socket socket = socketChannel.socket();
                  
                System.out.println(serverSocketChannel.socket().getLocalSocketAddress());
     
                // 立即注册一个 OP_READ 的SelectionKey, 接收客户端的消息
                SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ);             
                key.attach("第 " + clientCount + " 个客户端 [" + socket.getRemoteSocketAddress() + "]: ");
                p(key.p_w_upload() + "\t[connected] =========================================");
                } else if (selectionKey.isReadable()) {
                    // 有消息进来
                    ByteBuffer byteBuffer = ByteBuffer.allocate(100);
                    ByteBuffer byteBufferDD= ByteBuffer.allocate(100);
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
     
                    SocketChannel socketChannelDevice=(SocketChannel)map.get("/192.168.1.102").channel();
                    System.out.println(map.get("/192.168.1.102"));
                    //System.out.println(socketChannelDevice.socket().getPort());
                    try {
                        //向设备发指令
                        int instruction = socketChannel.read(byteBuffer);
                        int data=socketChannelDevice.read(byteBufferDD);
                        if (instruction > 0) {
                            byteBuffer.flip();//flip()方法用来将缓冲区准备为数据传出状态,这通过将limit设置为position的当前值,再 将position的值设为0来实现
                            //socketChannel.write(byteBuffer);
                            socketChannelDevice.write(byteBuffer);                         
                        }
                            //向外部连接传数据
                          
                              
                        else if (data > 0)
                        {
                            byteBuffer.flip();
                            socketChannel.write(byteBuffer);
                        } else {
                            // 输入结束,关闭 socketChannel
                            p(selectionKey.p_w_upload() + "read finished. close socketChannel. ");
                            socketChannel.close();
                        }
                    } catch (Exception e) {
                        // 如果read抛出异常,表示连接异常中断,需要关闭 socketChannel
                        e.printStackTrace();
                        p(selectionKey.p_w_upload() + "socket closed? ");
                        socketChannel.close();
                    }
                } else if (selectionKey.isWritable()) {
                    p(selectionKey.p_w_upload() + "TODO: isWritable() ???????????????????????????? ");
                } else if (selectionKey.isConnectable()) {
                    p(selectionKey.p_w_upload() + "TODO: isConnectable() ????????????????????????? ");
                } else {
                    p(selectionKey.p_w_upload() + "TODO: else. ");
                }
    }
    public static void p(Object object) {
        System.out.println(object);
    }
    public static void main(String[] args) throws Exception {
        new myServer();
    }
}