Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 588|回复: 0

开发交流:Android开发进阶之NIO非阻塞包(二)

[复制链接]

该用户从未签到

发表于 2011-10-24 10:50:29 | 显示全部楼层 |阅读模式
  有关Android NIO的精髓主要用于高负载的Socket网络传输,相对于传统I/O模型的Socket传输方式的优势,我们已经在 Android开发进阶之NIO非阻塞包(一)中讲到了,这里不再赘述,一起来看看Android NIO有关Socket操作提供的类吧:  一、ServerSocketChannel 服务器套接字通道在Android SDK中查找package名为  java.nio.channels.ServerSocketChannel
  在Java的NIO中,ServerSocketChannel对应的是传统IO中的ServerSocket,通过ServerSocketChannel类的socket() 方法可以获得一个传统的ServerSocket对象,同时从ServerSocket对象的getChannel() 方法,可以获得一个ServerSocketChannel()对象,这点说明NIO的ServerSocketChannel和传统IO的ServerSocket是有关联的,实例化ServerSocketChannel 只需要直接调用ServerSocketChannel 类的静态方法open()即可。
  二、 SocketChannel 套接字通道 java.nio.channels.SocketChannel
  在Java的New I/O中,处理Socket类对应的东西,我们可以看做是SocketChannel,套接字通道关联了一个Socket类,这一点使用SocketChannel类的socket() 方法可以返回一个传统IO的Socket类。SocketChannel()对象在Server中一般通过Socket类的getChannel()方法获得。
  三、SelectionKey 选择键 java.nio.channels.SelectionKey
  在NIO中SelectionKey和Selector是最关键的地方,SelectionKey类中描述了NIO中比较重要的事件,比如OP_ACCEPT(用于服务器端)、OP_CONNECT(用于客户端)、OP_READ和OP_WRITE。
  四、Selector 选择器 java.nio.channels.Selector
  在NIO中注册各种事件的方法主要使用Selector来实现的,构造一个Selector对象,使用Selector类的静态方法open()来实例化。
  对于Android平台上我们实现一个非阻塞的服务器,过程如下:
  1. 通过Selector类的open()静态方法实例化一个Selector对象。
  2. 通过ServerSocketChannel类的open()静态方法实例化一个ServerSocketChannel对象。
  3. 显示的调用ServerSocketChannel对象的configureBlocking(false);方法,设置为非阻塞模式,Android123提示网友这一步十分重要。
  4. 使用ServerSocketChannel对象的socket()方法返回一个ServerSocket对象,使用ServerSocket对象的bind()方法绑定一个IP地址和端口号
  5. 调用ServerSocketChannel对象的register方法注册感兴趣的网络事件,很多开发者可能发现Android SDK文档中没有看到register方法,这里Android开发网给大家一个ServerSocketChannel类的继承关系
  java.lang.Object
  ?java.nio.channels.spi.AbstractInterruptibleChannel
  ?java.nio.channels.SelectableChannel
  ?java.nio.channels.spi.AbstractSelectableChannel
  ?java.nio.channels.ServerSocketChannel
  这里我们使用的register方法其实来自ServerSocketChannel的父类java.nio.channels.SelectableChannel,该方法原型为 final SelectionKey  register(Selector selector, int operations)  ,参数为我们执行第1步时的selector对象,参数二为需要注册的事件,作为服务器,我们当然是接受客户端发来的请求,所以这里使用SelectionKey.OP_ACCEPT了。
  6. 通过Selector对象的select() 方法判断是否有我们感兴趣的事件发生,这里就是OP_ACCEPT事件了。我们通过一个死循环获取Selector对象执行select()方法的值,SDK中的原始描述为the number of channels that are ready for operation.,就是到底有多少个通道返回。
  7. 如果 Selector对象的select()方法返回的结果数大于0,则通过selector对象的selectedKeys()方法获取一个SelectionKey类型的Set集合,我们使用Java的迭代器Iterator类来遍历这个Set集合,注意判断SelectionKey对象,
  8. 为了表示我们处理了SelectionKey对象,需要先移除这个SelectionKey对象从Set集合中。这句很关键Android 123提醒网友注意这个地方。
  9. 接下来判断SelectionKey对象的事件,因为我们注册的感兴趣的是SelectionKey.OP_ACCEPT事件,我们使用SelectionKey对象的isAcceptable()方法判断,如果是我们创建一个临时SocketChannel对象类似上面的方法继续处理,不过这时这个SocketChannel对象主要处理读写操作,我们注册SelectionKey.OP_READ和SelectionKey.OP_WRITE分配ByteBuffer缓冲区,进行网络数据传输。
  我们通过一个实例详细讲解下Android下NIO非阻塞服务器的开发,对于客户端而言不推荐使用NIO,毕竟NIO相对于传统IO较为复杂,最重要的NIO是为了解决多线程并发问题而解决的技术,可能会因为管理和复杂性降低最终的结果,毕竟NIO是Java的,相关的类型比较难控制,对于客户端而言我们可以使用C++、Java、C#甚至Flash Action Script来编写。    下面我们以一个简单的Echo Server为例子来分析
import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.CharBuffer;

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.nio.charset.CharsetDecoder;

import java.nio.charset.CharsetEncoder;

import java.util.Iterator;

public class Server {

public static void main(String[] args) {

  Selector selector = null;

  ServerSocketChannel ssc = null;

  try {

      selector = Selector.open(); //实例化selector

      ssc = ServerSocketChannel.open(); //实例化ServerSocketChannel 对象

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

      ssc.configureBlocking(false); //设置为非阻塞模式

      ssc.register(selector, SelectionKey.OP_ACCEPT); //注册关心的事件,对于Server来说主要是accpet了



   while (true) {

   int n= selector.select(); //获取感兴趣的selector数量

   if(n<1)

          continue; //如果没有则一直轮训检查

    Iterator<SelectionKey> it = selector.selectedKeys().iterator(); //有新的链接,我们返回一个SelectionKey集合

    while (it.hasNext()) {

     SelectionKey key = it.next(); //使用迭代器遍历

     it.remove(); //删除迭代器

     if (key.isAcceptable()) { //如果是我们注册的OP_ACCEPT事件

      ServerSocketChannel ssc2 = (ServerSocketChannel) key.channel();

      SocketChannel channel = ssc2.accept();

      channel.configureBlocking(false); //同样是非阻塞

      channel.register(selector, SelectionKey.OP_READ); //本次注册的是read事件,即receive接受

      System.out.println("CWJ Client :" + channel.socket().getInetAddress().getHostName() + ":"  + channel.socket().getPort());

     }

    else if (key.isReadable()) { //如果为读事件



      SocketChannel channel = (SocketChannel) key.channel();

      ByteBuffer buffer = ByteBuffer.allocate(1024); //1KB的缓冲区

      channel.read(buffer); //读取到缓冲区

      buffer.flip(); //准备写入

      System.out.println("android123 receive info:" + buffer.toString());

      channel.write(CharBuffer.wrap("it works".getBytes())); //返回给客户端

     }

    }

   }

  } catch (IOException e) {

   e.printStackTrace();

  } finally {

   try {

    selector.close();

    server.close();

   } catch (IOException e) {

   }

  }

}

}
复制代码
上面是比较简单的框架,里面存在很多问题,将在下次详细阐述下,上面或者说国内有关NIO资料中的通病,如果你看过Mina或GlassFish的源码,你可能就知道上面的问题大于10种,有关框架的bug占了大多数,作为服务器而言很容易CPU超过100%


Android开发进阶之NIO非阻塞包(三)
Android开发进阶之NIO非阻塞包(四)
Android开发进阶之NIO非阻塞包(一)
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

GMT+8, 2025-1-11 08:15 , Processed in 0.302008 second(s), 36 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表