zoukankan      html  css  js  c++  java
  • java NIO经典实例

    服务端:

    Loader.java

    package net.chatroom.server;
    
    public class Loader {
    
        public static void main(String[] args) {
            Deamon deamon = new Deamon(9999);
            new Thread(deamon).start();
        }
    
    }

    Util.java

    package net.chatroom.server;
    
    import java.nio.charset.Charset;
    import java.util.HashSet;
    
    public class Util {
    
        public static Charset charset = Charset.forName("UTF-8");
        
        // 相当于自定义协议格式,与客户端协商好
        public static String USER_CONTENT_SPILIT = "#@#";
        
        // 用来记录在线人数,以及昵称
        public static HashSet<String> users = new HashSet<String>();
        public static String USER_EXIST = "system message: user exist, please change a name";
    }

    Deamon.java

    package net.chatroom.server;
    
    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.nio.channels.SocketChannel;
    import java.nio.charset.Charset;
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.List;
    
    public class Deamon implements Runnable {
    
        private boolean flag = true;
        
    
        private ServerSocketChannel serverChannel = null;
        private Selector selector = null;
        /**
         * 记录进来的所有的客户端连接
         * */
        private List<SocketChannel> clientChannels = null;
    
        public void setFlag(boolean flag) {
            this.flag = flag;
        }
    
        public Deamon(int port) {
            try {
                serverChannel = ServerSocketChannel.open();
                serverChannel.socket().bind(new InetSocketAddress(port));
                selector = Selector.open();
                serverChannel.configureBlocking(false);
                serverChannel.register(selector, SelectionKey.OP_ACCEPT);
                this.clientChannels = new ArrayList<SocketChannel>();
                System.out.println("Server is listening now...");
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
        @Override
        public void run() {
    //        System.out.println("server listening..");
            while (this.flag) {
                int num = 0;
                try {
                    //此处select()阻塞了线程
                    num = selector.select();
                } catch (IOException e) {
                    System.out.println("Error while select channel:" + e);
                }
                if (num > 0) {
                    Iterator<SelectionKey> it = selector.selectedKeys().iterator();
                    while (it.hasNext()) {
                        SelectionKey key = it.next();
                        it.remove();
                        if (key.isAcceptable()) {
                            // 监听到有新的连接则再注册读操作
                            this.clientChannels.add(Dealer.accept(selector,
                                    serverChannel));
                        } else if (key.isReadable()) {
                            // 监听到读操作
                            try {
                                Dealer.read(selector, key, clientChannels);
                            } catch (IOException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        }
                    }
                }
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("server to close..");
            if (this.serverChannel != null && this.serverChannel.isOpen()) {
                try {
                    this.serverChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            if (this.selector != null && this.selector.isOpen()) {
                try {
                    this.selector.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
    }

    Dealer.java

    package net.chatroom.server;
    
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.channels.Channel;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.rmi.server.Skeleton;
    import java.util.List;
    import java.util.Scanner;
    
    public class Dealer {
    
        public static SocketChannel accept(Selector selector,
                ServerSocketChannel serverChannel) {
            SocketChannel channel = null;
            try {
                channel = serverChannel.accept();
                channel.configureBlocking(false);
                channel.register(selector, SelectionKey.OP_READ);
    
                channel.write(Util.charset.encode("Please input your name."));
    
            } catch (Exception e) {
                System.out.println("Error while configure socket channel :" + e);
                if (channel != null) {
                    try {
                        channel.close();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                }
            }
            return channel;
        }
    
        public static void read(Selector selector, SelectionKey selectionkey,
                List<SocketChannel> clientChannels) throws IOException {
            SocketChannel socketClientChannel = (SocketChannel) selectionkey
                    .channel();
            ByteBuffer buffer = ByteBuffer.allocateDirect(6 * 1024);
            StringBuilder content = new StringBuilder();
            int num = 0;
            try {
                // 将客户端发上来的消息读到buffer
                //循环将通道中数据读入buffer
                while (socketClientChannel.read(buffer) > 0) {
                    buffer.flip();// 切换成读
                    content.append(Util.charset.decode(buffer));
                }
    
                System.out.println("num:" + num);
                System.out.println("Server is listening from client :"
                        + socketClientChannel.getRemoteAddress() + " data rev is: "
                        + content);
            } catch (IOException e) {
                /**
                 * 如果出现异常,则需要关闭连接。故把num设置为-1,用下面的关闭逻辑来关闭channel
                 */
                num = -1;
            }
    
            if (num >= 0) {
                if (content.length() > 0) {
                    String[] arrayContent = content.toString().split(
                            Util.USER_CONTENT_SPILIT);
                    // 注册用户
                    if (arrayContent != null && arrayContent.length == 1) {
                        String name = arrayContent[0];
                        if (Util.users.contains(name)) {
                            socketClientChannel.write(Util.charset
                                    .encode(Util.USER_EXIST));
                        } else {
                            Util.users.add(name);
                            int onlineNum = clientChannels.size();
                            String message = "welcome " + name
                                    + " to chat room! Online numbers:" + onlineNum;
                            BroadCast2(clientChannels, null, message);
                        }
                    }
                    // 注册完了,发送消息
                    else if (arrayContent != null && arrayContent.length > 1) {
                        String name = arrayContent[0];
                        String message = content.substring(name.length()
                                + Util.USER_CONTENT_SPILIT.length());
                        message = name + " say: " + message;
                        if (Util.users.contains(name)) {
                            // 不回发给发送此内容的客户端
                            BroadCast2(clientChannels, socketClientChannel, message);
                        }
                    }
    
                    // /**
                    // * 把读到的数据原封不动的下发给客户端
                    // */
                    // int length = clientChannels.size();
                    // for (int index = 0; index < length; index++) {
                    // // 循环所有的客户端连接,下发数据
                    // buffer.flip();
                    // try {
                    // // 将buffer里的数据再下发给客户端的通道
                    // clientChannels.get(index).write(buffer);
                    // } catch (IOException e) {
                    // e.printStackTrace();
                    // }
                    // }
                }
            } else {
                /**
                 * 如果未读到数据,对方关闭了SocketChannel 所以服务器这边也要关闭
                 */
                try {
                    socketClientChannel.close();
                    int length = clientChannels.size();
                    for (int index = 0; index < length; index++) {
                        if (clientChannels.get(index).equals(socketClientChannel)) {
                            // 移除当前接受的通道
                            clientChannels.remove(index);
                            break;
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
        }
    
        // TODO 要是能检测下线,就不用这么统计了
        public static int OnlineNum(Selector selector) {
            int res = 0;
            for (SelectionKey key : selector.keys()) {
                Channel targetchannel = key.channel();
    
                if (targetchannel instanceof SocketChannel) {
                    res++;
                }
            }
            return res;
        }
    
        public void BroadCast(Selector selector, SocketChannel except,
                String content) throws IOException {
            // 广播数据到所有的SocketChannel中
            for (SelectionKey key : selector.keys()) {
                Channel targetchannel = key.channel();
                // 如果except不为空,不回发给发送此内容的客户端
                if (targetchannel instanceof SocketChannel
                        && targetchannel != except) {
                    SocketChannel dest = (SocketChannel) targetchannel;
                    dest.write(Util.charset.encode(content));
                }
            }
        }
    
        public static void BroadCast2(List<SocketChannel> socketChannels,
                SocketChannel except, String content) throws IOException {
            for (SocketChannel socketChannel : socketChannels) {
                if (!socketChannel.equals(except)) {
                    // 除了自己,其它通道都通知
                    socketChannel.write(Util.charset.encode(content));
                }
            }
        }
    
    }

    客户端:

    Loader.java

    package net.chatroom.client;
    
    import java.util.Scanner;
    
    import net.chatroom.server.Util;
    
    public class Loader {
    
        public static void main(String[] args) {
            String name = "";
            Deamon deamon = new Deamon("127.0.0.1", 9999);
            new Thread(deamon).start();
    
            // 在主线程中 从键盘读取数据输入到服务器端
            Scanner scan = new Scanner(System.in);
            while (scan.hasNextLine()) {
                String line = scan.nextLine();
                if ("".equals(line))
                    continue; // 不允许发空消息
                if ("".equals(name)) {
                    name = line;
                    line = name + Util.USER_CONTENT_SPILIT;
                } else {
                    line = name + Util.USER_CONTENT_SPILIT + line;
                }
                deamon.chancelToWrite(Util.charset.encode(line));// sc既能写也能读,这边是写
            }
        }
    }

    Deamon.java

    package net.chatroom.client;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.Buffer;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    import java.util.Scanner;
    
    import net.chatroom.server.Util;
    
    public class Deamon implements Runnable {
        /**
         * 选择器,用于监听注册在上面的SocketChannel的状态
         */
        private Selector selector = null;
    
        /**
         * SocketChannel 用户发送和接受数据的信道
         */
        private SocketChannel channel = null;
    
        /**
         * 运行标识。在线程里此标识为false的时候会推出线程
         * 该属性在ExitCommandListener里通过调用setFlag方法修改,用于通知线程用户要求退出的程序
         */
        private boolean flag = true;
    
        public void setFlag(boolean flag) {
            this.flag = flag;
        }
    
        public Deamon(String address, int port) {
            try {
                channel = SocketChannel.open(new InetSocketAddress(address, port));
                channel.configureBlocking(false);
                selector = Selector.open();
                // 客户端直接注册读和写操作
                channel.register(selector, SelectionKey.OP_READ
                        | SelectionKey.OP_WRITE);
    
                
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
        
        public void chancelToWrite(ByteBuffer buffer){
            try {
                channel.write(buffer);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
        @Override
        public void run() {
            System.out.println("client run..");
            while (this.flag) {
                /**
                 * 如果可以继续执行,则在循环体内循环执行监听选择操作
                 */
                int num = 0;
                try {
                    /**
                     * 得到处于可读或者可写状态的SocketChannel对象的个数
                     */
                    // 客户端的select()并不阻塞线程,是因为客户端一启动就是SelectionKey.OP_WRITE状态
    //                 System.out.println("client select..");
                    num = this.selector.select();
    
    //                 System.out.println("client num:"+num);
                } catch (IOException e) {
                    /**
                     * 如果出现异常,则此处应该加上日志打印,然后跳出循环,执行循环体下面的释放资源操作
                     */
                    break;
                }
    
                if (num > 0) {
                    /**
                     * 如果有多个SocketChannel处于可读或者可写状态,则轮询注册在Selector上面的SelectionKey
                     */
                    Iterator<SelectionKey> keys = selector.selectedKeys()
                            .iterator();
                    while (keys.hasNext()) {
                        SelectionKey key = keys.next();
                        /**
                         * 此步操作用于删除该SelectionKey的被选中状态
                         */
                        keys.remove();
                        if (key.isReadable()) {
                            System.out.println("client isReadable..");
                            /**
                             * 如果是读操作,则调用读操作的处理逻辑
                             */
                            try {
                                Dealer.read((SocketChannel) key.channel());
                            } catch (IOException e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }
                        } else if (key.isWritable()) {
                            //客户端的写状态是一直就绪的
                            // System.out.println("client isWritable..");
                            /**
                             * 如果是写操作,则调用写操作的处理逻辑
                             */
    //                        Dealer.write((SocketChannel) key.channel());
                        }
                    }
                }
                
                /*取消关注,多用在多线程的时候
                 * key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));
                 * 
                 * 增加关注
                 * key.interestOps(key.interestOps() | SelectionKey.OP_READ);
                 * */
    
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
    
            if (this.channel != null && this.channel.isOpen()) {
                /**
                 * 关闭SocketChannel
                 */
                try {
                    this.channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            if (this.selector != null && this.selector.isOpen()) {
                /**
                 * 关闭Selector选择器
                 */
                try {
                    this.selector.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    Dealer.java

    package net.chatroom.client;
    
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.channels.SocketChannel;
    
    import net.chatroom.server.Util;
    
    public class Dealer {
        /**
         * 从SocketChannel中读取信息
         * 
         * @param channel
         * @throws IOException 
         */
        public static void read(SocketChannel channel) throws IOException {
    
            /**
             * 初始化缓冲区
             */
            ByteBuffer buffer = ByteBuffer.allocateDirect(6 * 1024);
            /**
             * 读到的字节数
             */
            int num = 0;
            String content = "";
            while ((num = channel.read(buffer)) > 0) {
                buffer.flip();
                content += Util.charset.decode(buffer);
            }
            //若系统发送通知名字已经存在,则需要换个昵称
            if(Util.USER_EXIST.equals(content)) {
    //            name = "";
                System.out.println("name has exists.");
            }
            System.out.println(content);
        }
    
        /**
         * 想SocketChannel中写入数据
         * 
         * @param channel
         */
        public static void write(SocketChannel channel) {
    
    //        /**
    //         * 从消息队列中获取要发送的消息
    //         */
    //        String msg = MsgQueue.getInstance().get();
    //        if (msg == null) {
    //            /**
    //             * 如果消息队列中没有要发送的消息,则返回。
    //             */
    //            return;
    //        }
    //        /**
    //         * 初始化缓冲区
    //         */
    //        ByteBuffer buffer = ByteBuffer.allocateDirect(6 * 1024);
    //
    //        /**
    //         * 把消息放到缓冲区中
    //         */
    //        buffer.put(msg.getBytes());
    //
    //        /**
    //         * 重置缓冲区指针
    //         */
    //        buffer.flip();
    //        try {
    //            /**
    //             * 把缓冲区中的数据写到SocketChannel里
    //             */
    //            channel.write(buffer);
    //        } catch (IOException e) {
    //            e.printStackTrace();
    //        }
        }
    }
  • 相关阅读:
    MSSQL 事务说明
    创业课堂之团队
    如何开发HTML编辑器
    IE和Firefox对Documnet,iframe的处理
    jQuery控制iFrame
    如何更高效的制作可通用的HTML页面
    天下武功,无坚不破,唯快不破
    Flash本地通讯
    播放本地MP3 (二)
    播放本地MP3 (一)
  • 原文地址:https://www.cnblogs.com/zhuawang/p/3843723.html
Copyright © 2011-2022 走看看