NIO Dome

服務端

public class Server {

    // 通道管理器
    private Selector selector;

    /**
     * 启动服务端测试
     *
     * @throws IOException
     */
    public static void main(String[] args) throws IOException, InterruptedException {
        Server server = new Server();
        server.initServer(12345);
        server.listen();
    }

    /**
     * 获得一个ServerSocket通道,并对该通道做一些初始化的工作
     *
     * @param port
     *            绑定的端口号
     * @throws IOException
     */
    public void initServer(int port) throws IOException {
        // 获得一个ServerSocket通道
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        // 设置通道为非阻塞(与select一起使用必须为非阻塞)
        serverChannel.configureBlocking(false);
        // 将该通道对应的ServerSocket绑定到port端口
        serverChannel.socket().bind(new InetSocketAddress(port));
        // 获得一个通道管理器
        this.selector = Selector.open();
        // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
        // 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    /**
     * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
     *
     * @throws IOException
     */
    public void listen() throws IOException, InterruptedException {
        System.out.println("服务端启动成功!");
        // 轮询访问selector
        while (true) {
            // 当注册的事件到达时,方法返回;否则,该方法会一直阻塞
            selector.select();
            // 获得selector中选中的项的迭代器,选中的项为注册的事件
            Iterator<?> ite = this.selector.selectedKeys().iterator();
            while (ite.hasNext()) {
                SelectionKey key = (SelectionKey) ite.next();
                // 由于select操作只管对selectedKeys进行添加,所以key处理后我们需要从里面把key去掉
                ite.remove();
                handler(key);
            }
            System.out.println("結束本次select的查詢");
        }
    }

    /**
     * 处理请求
     *
     * @param key
     * @throws IOException
     */
    public void handler(SelectionKey key) throws IOException {
        // 客户端请求连接事件
        if (key.isAcceptable()) {
            handlerAccept(key);
            // 获得了可读的事件
        } else if (key.isReadable()) {
            handelerRead(key);
        }
    }

    /**
     * 处理连接请求
     *
     * @param key
     * @throws IOException
     */
    public void handlerAccept(SelectionKey key) throws IOException {
        ServerSocketChannel server = (ServerSocketChannel) key.channel();
        // 获得和客户端连接的通道
        SocketChannel channel = server.accept();
        // 设置成非阻塞
        channel.configureBlocking(false);
        // 在这里可以给客户端发送信息哦
        System.out.println("新的客户端连接");
        // 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
        channel.register(this.selector, SelectionKey.OP_READ);
    }

    /**
     * 处理读的事件
     *
     * @param key
     * @throws IOException
     */
    public void handelerRead(SelectionKey key) throws IOException {
        // 服务器可读取消息:得到事件发生的Socket通道
        SocketChannel channel = (SocketChannel) key.channel();
        // 创建读取的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int read = 0;
        try {
            read = channel.read(buffer);
        } catch (IOException e) {
            key.cancel();
            channel.close();
            return ;
        }
        String content = "";
        if(read > 0){
            buffer.flip(); //为write()准备
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            content+=new String(bytes);
            System.out.println("收到百果園客戶的問題:" + content);
            //回应客户端
            doWrite(channel);
        }else{
            System.out.println("客户端关闭");
            key.cancel();
        }
        // 写完就把状态关注去掉,否则会一直触发写事件(改变自身关注事件)
        key.interestOps(SelectionKey.OP_READ);
    }

    private void doWrite(SocketChannel sc) throws IOException{
        String content = "百果園的水果是天下最好吃的。";
        byte[] req =content.getBytes();
        ByteBuffer byteBuffer = ByteBuffer.allocate(req.length);
        byteBuffer.put(req);
        byteBuffer.flip();
        sc.write(byteBuffer);
        if(!byteBuffer.hasRemaining()){
            System.out.println("回答百果園客戶的問題:" + content);
        }
    }

}

客戶端

public class SocketClient {

    /**
     * 服务器地址
     */
    public static final String IP_ADDR = "localhost";

    private Selector selector = null;
    
    private volatile boolean stop = false;

    /**
     * 服务器端口号
     */
    public static final int PORT = 12345;

    public static void main(String[] args) throws IOException {
        System.out.println("客户端启动");
        SocketClient client = new SocketClient();
        client.init();
    }

    public void init() throws IOException {
        selector = Selector.open();
        SocketChannel channel = SocketChannel.open();
        // 设置为非阻塞模式,这个方法必须在实际连接之前调用(所以open的时候不能提供服务器地址,否则会自动连接)
        channel.configureBlocking(false);

        if (channel.connect(new InetSocketAddress(12345))) {
            channel.register(selector, SelectionKey.OP_READ);
            //发送消息
            doWrite(channel, "百果園的水果好吃嗎 ??");
        } else {
            channel.register(selector, SelectionKey.OP_CONNECT);
        }
        while (!stop) {
            selector.select(1000);
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> it = keys.iterator();
            SelectionKey key = null;
            while (it.hasNext()) {
                key = it.next();
                it.remove();
                handler(key);
            }
        }

    }

    private void handler(SelectionKey key) {
        // OP_CONNECT 两种情况,链接成功或失败这个方法都会返回true
        if (key.isConnectable()) {
            handlerConnection(key);
        }else if (key.isReadable()) {
            handlerReader(key);
        }
    }

    /**
     * 处理连接请求
     *
     * @param key
     * @throws IOException
     */
    public void handlerConnection(SelectionKey key)  {
        SocketChannel channel = (SocketChannel) key.channel();
        // 由于非阻塞模式,connect只管发起连接请求,finishConnect()方法会阻塞到链接结束并返回是否成功
        // 另外还有一个isConnectionPending()返回的是是否处于正在连接状态(还在三次握手中)
        try {
            if (channel.finishConnect()) {
                channel.register(selector, SelectionKey.OP_READ);
                //    new Thread(new DoWrite(channel)).start();
                doWrite(channel, "百果園的水果好吃嗎 ??");
            } else {
                //链接失敗
                channel.close();
                key.cancel();
            }
        } catch (IOException e) {
            //链接失败,进程推出或直接抛出IOException
            System.exit(1);
        }
    }

    /**
     * 处理讀任務
     *
     * @param key
     * @throws IOException
     */
    public void handlerReader(SelectionKey key) {
        SocketChannel channel = (SocketChannel) key.channel();
        //读取服务端的响应
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        String content = "";
        try {
            int readBytes = channel.read(buffer);
            if (readBytes > 0) {
                buffer.flip();
                byte[] bytes = new byte[buffer.remaining()];
                buffer.get(bytes);
                content += new String(bytes);
                stop = true;
                System.out.println(content);
                key.interestOps(SelectionKey.OP_READ);

            } else if (readBytes < 0) {
                //对端链路关闭
                key.cancel();
                channel.close();
            }
        } catch (IOException e) {
            //讀取數據出現IO Exception, 进程推出或直接抛出IOException
            System.exit(1);
        }
    }

    private  void doWrite(SocketChannel sc,String data) throws IOException{
        byte[] req =data.getBytes();
        ByteBuffer byteBuffer = ByteBuffer.allocate(req.length);
        byteBuffer.put(req);
        byteBuffer.flip();
        sc.write(byteBuffer);
        if(!byteBuffer.hasRemaining()){
            System.out.println("發送百果園客戶的問題: " + data);
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值