Java NIO 非阻塞socket通信案例

NIO的特性:它以块为基本单位处理数据,所有的数据都要通过缓冲区(Buffer)来进行传输。它有一个用来作为原始I/O操作的抽象通道(Channel)并提供了Selector的异步网络接口。且支持将文件映射到内存,以大幅提高I/O效率。
缓冲区中有3个重要的参数:
position(位置):即缓冲区的位置,指缓冲区指针到哪个位置了。
capactiy(容量):缓冲区的总上限,如ByteBuffer.allocate(2048),则认为产生的这个bytebuffer的容量为2048
limit(上限):实际存入缓冲区数据所占的容量,也称为实际上限。
常用方法:
filp():将position置为0,将limit置为原position位置。经过此操作的buffer数据才能被正常读取(读写转换)。
clear():将position置为0,limit设置为容量大小,并清除mark标志位。效果是清空buffer
rewind():将position置为0,并清除标志位
文件映射到内存的方法:

RandomAccessFile raf = new RandomAccessFile("C:\\test.dat", "rw");
FileChannel fc = raf.getChannel();
//将文件映射到内存中
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, 0, raf.length()); while(mbb.hasRemaining()){
System.out.print((char)mbb.get()); }
mbb.put(0,(byte)98); //修改文件
raf.close();

正文:
Nio非阻塞网络通信的工作原理为,由一个线程监听多个客户端,由selector选择已完成数据准备工作的客户端,以对请求做出处理。由一个线程来监听多个客户端的方式能够省下来大量的线程资源。

以下是模拟一次计算请求的代码:
服务端:

public class NioServer {
        private Selector selector;
        private ByteBuffer recive = ByteBuffer.allocate(2048);
        private ByteBuffer send = ByteBuffer.allocate(2048);

        public NioServer(int port) throws IOException {
            ServerSocketChannel ssc = ServerSocketChannel.open();
            // 设置非阻塞
            ssc.configureBlocking(false);
            ServerSocket socket = ssc.socket();
            socket.bind(new InetSocketAddress(port));
            selector = Selector.open();
            ssc.register(selector, SelectionKey.OP_ACCEPT);
        }

        /**
         * 启动服务器
         * 
         * @throws IOException
         */
        public void startup() throws IOException {
            System.out.println("=======Nio Server is started!=======");
            for (;;) {
                int size = selector.select();
                if (size > 0) {
                    Set<SelectionKey> keys = selector.selectedKeys();
                    for (Iterator<SelectionKey> iterator = keys.iterator(); iterator.hasNext();) {
                        SelectionKey key = (SelectionKey) iterator.next();
                        iterator.remove();
                        processor(key);
                    }
                }
            }

        }

        /**
         * 停止服务器
         */
        public void shutdown() {
            System.out.println("=======Nio Server is stoped!=======");
            System.exit(0);
        }

        /**
         * 消息处理
         * 
         * @throws IOException
         */
        public void processor(SelectionKey key) throws IOException {
            if (key.isAcceptable()) {
                ServerSocketChannel server = (ServerSocketChannel) key.channel();
                SocketChannel accept = server.accept();
                accept.configureBlocking(false);
                // 改变监听状态
                accept.register(selector, SelectionKey.OP_READ);
            } else if (key.isReadable()) {
                SocketChannel client = (SocketChannel) key.channel();
                recive.clear(); // 在读取数据之前要保证缓冲区是空的
                int size = client.read(recive); //读取完成后返回-1
                if(size == -1) {
                    //如果没读出来东西则不做任何操作
                } else {
                    client.register(selector, SelectionKey.OP_WRITE);
                }
            } else if (key.isWritable()) {
                SocketChannel client = (SocketChannel) key.channel();
                int value = arithmetic(new String(recive.array())); //计算结果[业务部分]
                byte[] result = String.valueOf(value).getBytes();
                send.clear();
                send.put(result);
                send.flip();
                // 返回结果
                client.write(send);
                client.register(selector, SelectionKey.OP_READ);  //如有大量数据要处理,设置此项 循环处理。
            }
        }

        /**
         * 计算结果
         * 
         * @return
         */
        private int arithmetic(String data) {
            //假设这里面有一堆操作
            System.out.println("客户端数据:"+data);//结果为空,转换缓冲区没报错
            return 1;
        }




        public static void main(String[] args) {
            try {
                NioServer nioServer = new NioServer(9900);
                nioServer.startup();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
}

客户端:

public class NioClient {
    private int port;
    private ByteBuffer recive = ByteBuffer.allocate(2048);
    private ByteBuffer send = ByteBuffer.allocate(2048);

    public NioClient(int port) throws IOException {
        this.port = port;

    }

    /**
     * 发送数据并等待运算结果
     * 
     * @param obj
     * @return
     */
    public Object send(Object obj) throws IOException {
        SocketChannel sc = SocketChannel.open();
        sc.configureBlocking(false);
        Selector selector = Selector.open();
        sc.connect(new InetSocketAddress(port));
        sc.register(selector, SelectionKey.OP_CONNECT);
        Object result = null;
        boolean isDone = false;
        for (;;) {
            if (selector.select() == 0) {
                continue;
            }
            Set<SelectionKey> keys = selector.selectedKeys();
            for (Iterator<SelectionKey> iterator = keys.iterator(); iterator.hasNext();) {
                SelectionKey key = (SelectionKey) iterator.next();
                iterator.remove();
                sc = (SocketChannel) key.channel();
                if (key.isConnectable()) {
                    if (sc.isConnectionPending()) {
                        sc.finishConnect();
                        sc.register(selector, SelectionKey.OP_WRITE);  
                    }
                } else if (key.isReadable()) {
                    recive.clear();
                    sc.read(recive);
                    result = new String(recive.array());
                    System.out.println("done!");
                    sc.register(selector, SelectionKey.OP_WRITE);  
                    isDone = true;
                    sc.close();
                } else if (key.isWritable()) {
                    byte[] datas = ((String)obj).getBytes();
                    send.clear();
                    send.put(datas);
                    //装载完数据要恢复缓冲区标志位,不然服务端会在读取时出现问题
                    send.flip();
                    sc.write(send);
                    sc.register(selector, SelectionKey.OP_READ);  
                }
            } //iterator
            if(isDone == true) {
                break;
            }
        } //for true

        return result;
    }

    public static void main(String[] args) {
        try {
        NioClient client = new NioClient(9900); //连接四则运算服务器

        Object result = client.send("1+1");

        System.out.println("计算结果为:"+ String.valueOf(result));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

源码下载地址:http://download.csdn.net/detail/shf4715/9838492

如有错误或不合理的地方,烦请大家留言,本人会尽快改正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值