java中NIO编程另一种实现超实用

  除了普通的Socket与ServerSocket实现的阻塞式通信外,Java提供了非阻塞式通信的NIO API。先看一下NIO的实现原理。

 

       从图中可以看出,服务器上所有Channel(包括ServerSocketChannel和SocketChannel)都需要向Selector注册,而该Selector则负责监视这些Socket的IO状态,当其中任意一个或者多个Channel具有可用的IO操作时,该Selector的select()方法将会返回大于0的整数,该整数值就表示该Selector上有多少个Channel具有可用的IO操作,并提供了selectedKeys()方法来返回这些Channel对应的SelectionKey集合。正是通过Selector,使得服务器端只需要不断地调用Selector实例的select()方法即可知道当前所有Channel是否有需要处理的IO操作。

看个demo

NClient.java

 

[java]  view plain  copy
  1. import java.io.IOException;  
  2. import java.net.InetSocketAddress;  
  3. import java.nio.ByteBuffer;  
  4. import java.nio.channels.SelectionKey;  
  5. import java.nio.channels.Selector;  
  6. import java.nio.channels.SocketChannel;  
  7. import java.nio.charset.Charset;  
  8. import java.util.Scanner;  
  9.   
  10. public class NClient {  
  11.     //定义检测SocketChannel的Selector对象  
  12.     private Selector selector=null;  
  13.     //定义处理编码和解码的字符集  
  14.     private Charset charset=Charset.forName("UTF-8");  
  15.     //客户端SocketChannel  
  16.     private SocketChannel sc=null;  
  17.     public void init() throws IOException{  
  18.         selector=Selector.open();  
  19.         InetSocketAddress isa=new InetSocketAddress("127.0.0.1",30000);  
  20.         //调用open静态方法创建连接到指定主机的SocketChannel  
  21.         sc=SocketChannel.open(isa);  
  22.         //设置该sc以非阻塞方式工作  
  23.         sc.configureBlocking(false);  
  24.         //将Socketchannel对象注册到指定Selector  
  25.         sc.register(selector, SelectionKey.OP_READ);  
  26.         //启动读取服务器端数据的线程  
  27.         new ClientThread().start();  
  28.         //创建键盘输入流  
  29.         Scanner scan=new Scanner(System.in);  
  30.         while(scan.hasNextLine()){  
  31.             //读取键盘输入  
  32.             String line=scan.nextLine();  
  33.             //将键盘输入的内容输出到SocketChannel中  
  34.             sc.write(charset.encode(line));  
  35.         }  
  36.     }  
  37.     //定义读取服务器数据的线程  
  38.     private class ClientThread extends Thread{  
  39.         public void run(){  
  40.             try{  
  41.                 while(selector.select()>0){  
  42.                     //遍历每个有可用IO操作Channel对应的SelectionKey  
  43.                     for(SelectionKey sk:selector.selectedKeys()){  
  44.                         //删除正在处理的SelectionKey  
  45.                         selector.selectedKeys().remove(sk);  
  46.                         //如果该SelectionKey对应的Channel中有可读的数据  
  47.                         if(sk.isReadable()){  
  48.                             //使用NIO读取channel中的数据  
  49.                             SocketChannel sc=(SocketChannel) sk.channel();  
  50.                             ByteBuffer buff=ByteBuffer.allocate(1024);  
  51.                             String content="";  
  52.                             while(sc.read(buff)>0){  
  53.                                 //sc.read(buff);  
  54.                                 buff.flip();  
  55.                                 content+=charset.decode(buff);  
  56.                             }  
  57.                             //打印输出读取的内容  
  58.                             System.out.println("聊天信息"+content);  
  59.                             //为下一次读取做准备  
  60.                             sk.interestOps(SelectionKey.OP_READ);  
  61.                         }  
  62.                     }  
  63.                 }  
  64.             }catch(IOException ex){  
  65.                 ex.printStackTrace();  
  66.             }  
  67.         }  
  68.     }  
  69.     public static void main(String[]args) throws IOException{  
  70.         new NClient().init();  
  71.     }  
  72. }  

NServer.java

[java]  view plain  copy
  1. import java.io.IOException;  
  2. import java.net.InetSocketAddress;  
  3. import java.nio.ByteBuffer;  
  4. import java.nio.channels.Channel;  
  5. import java.nio.channels.SelectionKey;  
  6. import java.nio.channels.Selector;  
  7. import java.nio.channels.ServerSocketChannel;  
  8. import java.nio.channels.SocketChannel;  
  9. import java.nio.charset.Charset;  
  10.   
  11. public class NServer {  
  12.     //用于检测所有Channel状态的Selector  
  13.     private Selector selector=null;  
  14.     //定义实现编码、解码的字符集对象  
  15.     private Charset charset=Charset.forName("UTF-8");  
  16.     public void init() throws IOException{  
  17.         selector=Selector.open();  
  18.         //通过open方法来打开一个未绑定的ServerSocketChannel实例  
  19.         ServerSocketChannel server=ServerSocketChannel.open();  
  20.         InetSocketAddress isa=new InetSocketAddress("127.0.0.1",30000);  
  21.         //将该ServerSocketChannel绑定到指定ip地址  
  22.         server.socket().bind(isa);  
  23.         //设置ServerSocket以非阻塞方式工作  
  24.         server.configureBlocking(false);  
  25.         //将server注册到指定Selector对象  
  26.         server.register(selector, SelectionKey.OP_ACCEPT);  
  27.         while(selector.select()>0){  
  28.             //依次处理selector上的每个已选择的SelectionKey  
  29.             for(SelectionKey sk:selector.selectedKeys()){  
  30.                 //从selector上的已选择Key集中删除正在处理的SelectionKey  
  31.                 selector.selectedKeys().remove(sk);  
  32.                 //如果sk对应的通信包含客户端的连接请求  
  33.                 if(sk.isAcceptable()){  
  34.                     //调用accept方法接受连接,产生服务器端对应的SocketChannel  
  35.                     SocketChannel sc=server.accept();  
  36.                     //设置采用非阻塞模式  
  37.                     sc.configureBlocking(false);  
  38.                     sc.register(selector, SelectionKey.OP_READ);  
  39.                     //将sk对应的Channel设置成准备接受其他请求  
  40.                     sk.interestOps(SelectionKey.OP_ACCEPT);  
  41.                 }  
  42.                 //如果sk对应的通道有数据需要读取  
  43.                 if(sk.isReadable()){  
  44.                     //获取该SelectionKey对应的Channel,该Channel中有可读的数据  
  45.                     SocketChannel sc=(SocketChannel) sk.channel();  
  46.                     //定义准备之星读取数据的ByteBuffer  
  47.                     ByteBuffer buff=ByteBuffer.allocate(1024);  
  48.                     String content="";  
  49.                     //开始读取数据  
  50.                     try{  
  51.                         while(sc.read(buff)>0){  
  52.                             buff.flip();  
  53.                             content+=charset.decode(buff);  
  54.                         }  
  55.                         //打印从该sk对应的Channel里读到的数据  
  56.                         System.out.println("=========="+content);  
  57.                         //将sk对应的Channel设置成准备下一次读取  
  58.                         sk.interestOps(SelectionKey.OP_READ);  
  59.                         //如果捕捉到该sk对应的channel出现异常,即表明该channel对应的client出现了  
  60.                         //异常,所以从selector中取消sk的注册  
  61.                     }catch(IOException e){  
  62.                         //从Selector中删除指定的SelectionKey  
  63.                         sk.cancel();  
  64.                         if(sk.channel()!=null){  
  65.                             sk.channel().close();  
  66.                         }  
  67.                     }  
  68.                     //如果content的长度大于0,即聊天信息不为空  
  69.                     if(content.length()>0){  
  70.                         //遍历该selector里注册的所有SelectKey  
  71.                         for(SelectionKey key:selector.keys()){  
  72.                             //选取该key对应的Channel  
  73.                             Channel targetChannel=key.channel();  
  74.                             //如果该channel是SocketChannel对象  
  75.                             if(targetChannel instanceof SocketChannel){  
  76.                                 //将独到的内容写入该Channel中  
  77.                                 SocketChannel dest=(SocketChannel) targetChannel;  
  78.                                 dest.write(charset.encode(content));  
  79.                             }  
  80.                         }  
  81.                     }  
  82.                 }  
  83.             }  
  84.         }  
  85.     }  
  86.     public static void main(String[]args) throws IOException{  
  87.         new NServer().init();  
  88.     }  
  89. }  


          通过java提供的NIO实现非阻塞Socket通信,大大提高了网络服务器的性能。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值