java nio实现非阻塞Socket通信实例

服务器

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

客户端

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