Chinaunix首页 | 论坛 | 博客
  • 博客访问: 83194
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 340
  • 用 户 组: 普通用户
  • 注册时间: 2013-04-02 20:25
文章分类

全部博文(31)

文章存档

2015年(2)

2014年(29)

我的朋友

分类: Java

2014-09-09 18:52:25

  多线程是解决socket的阻塞问题的一种方法,但该方法的问题是线程的最大数一般有限制,而且多个线程频繁的上下文切换需要消耗大量的系统资源。一个解决方法是采用线程池,但对于长连接采用线程池显然无法解决问题,通常采用的方式为NIO(非阻塞IO或IO复用)、AIO(异步IO)。java在1.7版本的时候开始支持AIO,BIO跟NIO早已支持。
 linux中io复用有select/poll跟epoll两种方式。select的缺陷是监听的文件描述符数量有限制,并且每次select调用会把描述符从用户空间拷贝到内核,有事件通知时需要遍历整个描述符集合。而epoll解决了select这两个缺陷,它只通知有事件发生的文件描述符,通常epoll的性能比select要高。另外作为非阻塞IO, select或者epoll_wait仍为阻塞函数,若该函数返回,表示其监听的描述符中有一个或多个有事件发生,比如有一个客户端连接、有读写事件等, 而接受到通知并不表示IO事件已经完成,表示我们可以进行相应IO操作了,此时该操作不阻塞。NIO的编程模型如下:

点击(此处)折叠或打开

  1. while(true){
  2.   finfds = select(fds);
  3.   for(fd : fdsfinfds ){
  4.     if(fd is readable) handleRead(fd);
  5.     else if(fd is writeable) handleWrite(fd);
  6.     else if(fd is acceptable) handleAccept(fd);
  7.     else
  8.        ....
  9.   }
  10. }

一、Java NIO

    java的NIO主要有3个部分组成:
1. 缓冲区(Buffer):实际上就是一个经过包装的数组
2. 通道(Channel):相当于一根水管,连接IO设备与Buffer。跟输入输出流很相似
3. 监听器(Selector):监听器,监听IO设备是否就绪

二、Channel

  Channel根据作用不同分为
1、FileChannel:文件读写。FileChannel总是阻塞的,所以NIO应该是new io的简称
2、ServerSocketChannel:用于监听TCP连接的请求
3、SocketChannel:处理TCP读写事件
4、
DatagramChannel:处理UDP
 Channel总是配合Buffer使用,FileChannel例子如下: 

点击(此处)折叠或打开

  1. File file = new File(Thread.currentThread().getContextClassLoader()
  2.                 .getResource("1.txt").getPath());

  3.         RandomAccessFile f = new RandomAccessFile(file, "rw");
  4.         FileChannel channel = f.getChannel();

  5.         ByteBuffer bytes = ByteBuffer.allocate(1024);
  6.         CharBuffer chars = CharBuffer.allocate(1024);

  7.         Charset charset = Charset.forName("GBK");
  8.         CharsetDecoder decoder = charset.newDecoder();
  9.         int reads = channel.read(bytes);
  10.         while (reads != -1) {
  11.             bytes.flip();
  12.             chars = decoder.decode(bytes);
  13.             
  14.             
  15.             while(chars.hasRemaining())
  16.                 System.out.print(chars.get());
  17.             chars.flip();
  18.             
  19.             bytes.clear();
  20.             chars.clear();
  21.             reads = channel.read(bytes);
  22.         }
  23.         f.close();

三、Buffer

   Buffer有很多种,但本质都是对数组的包装。内部结构如图:

1. capacity: 总容量
2. position:当前读或者写的位置
3. limit:写时表示还剩容量,读时表示总可读容量,在写转读时,设置limit=position
4. filp():写转读。limit=position,position=0
5. clear():清空缓存区 position=0,limit=capacity
6. rewind():position=0, 重读数据
7. compact():将为读完的数据拷贝到缓存起始部分,设置position为最后一个元素后面
8. mark()与reset():一个old = position值,一个position=old
9. equals():针对缓冲区而已,跟里面元素无关。类型一致并且剩余容量相同,认为相等。
9. compareTo():比较元素
10. DirectByteBuffer:这是跟其他缓冲有本质区别的一种缓冲区。实际上所有的通道操作都需要一个DirectByteBuffer。过程:  

    创建临时的direct ByteBuffer

    复制non-direct buffer中的内容到临时buffer

    使用临时buffer执行IO操作

    临时buffer不被引用,最终被垃圾收集

四、Selector

  selector是一个监听器,一个channel(必须配置成非阻塞)可以自己感兴趣的事件绑定到selector上. select阻塞返回会生成一系列SelectionKey对象,该对象描述了通道的一些属性,比如interest跟ready集合,另外还可以携带一个附加对象。selector监听的事件如下:

1. OP_ACCEPT: 连接请求
2. OP_READ:读
3. OP_WRITE:这个比较特殊。通常写缓冲区都是有空闲空间的,那么会一直才生该事件。所以一般做法是在真正需要写的时候注册该事件,写完后取消该事件
4. OP_CONNECT:客户端连接成功

点击(此处)折叠或打开

  1. Selector selector = Selector.open();
  2. channel.configureBlocking(false);
  3. SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
  4. while(true) {
  5.   int readyChannels = selector.select();
  6.   if(readyChannels == 0) continue;
  7.   Set selectedKeys = selector.selectedKeys();
  8.   Iterator keyIterator = selectedKeys.iterator();
  9.   while(keyIterator.hasNext()) {
  10.     SelectionKey key = keyIterator.next();
  11.     if(key.isAcceptable()) {
  12.         // a connection was accepted by a ServerSocketChannel.
  13.     } else if (key.isConnectable()) {
  14.         // a connection was established with a remote server.
  15.     } else if (key.isReadable()) {
  16.         // a channel is ready for reading
  17.     } else if (key.isWritable()) {
  18.         // a channel is ready for writing
  19.     }
  20.     keyIterator.remove();
  21.   }
  22. }




阅读(898) | 评论(0) | 转发(0) |
0

上一篇:1. 地址与Socket

下一篇:3. NIO--echoserver

给主人留下些什么吧!~~