用nio实现Echo服务

51CTO.COM · · 102 次点击 · · 开始浏览    

今天突然间想用nio实现个Echo服务,程序实现起来实现不算困难,但跑起来后,在Server端的ServerSocket完成accept之后,我的CPU总是跳到100%。嗯,小郁闷,后来,才发现自己在Server端注册了多余的监听事件SelectionKey.OP_WRITE,改过来后好多了,希望记住这个教训。

EchoServer.java

  1. package edu.dlut.zxf.nio;  
  2.  
  3. import java.io.IOException;  
  4. import java.net.InetAddress;  
  5. import java.net.InetSocketAddress;  
  6. import java.nio.ByteBuffer;  
  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.util.Set;  
  12.  
  13. /**  
  14.  * Echo服务器  
  15.  * @author finux  
  16.  */ 
  17. public class EchoServer {  
  18.     public final static int BUFFER_SIZE = 1024//默认端口  
  19.     public final static String HOST = "210.30.107.17";  
  20.     public final static int PORT = 8888;  
  21.       
  22.     public static void main(String[] args) {  
  23.         ServerSocketChannel ssc = null;  
  24.         //缓冲区  
  25.         ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);  
  26.         Selector selector = null;  
  27.         try {  
  28.             selector = Selector.open();  
  29.             ssc = ServerSocketChannel.open();  
  30.             ssc.socket().bind(new InetSocketAddress(InetAddress.getByName(HOST), PORT));  
  31.             ssc.configureBlocking(false);  
  32.             ssc.register(selector, SelectionKey.OP_ACCEPT);       
  33.             print("服务器启动,准备好连接...");  
  34.             while (selector.select() > 0) {       
  35.                 Set<SelectionKey> selectionKeys = selector.selectedKeys();  
  36.                 for (SelectionKey key: selectionKeys) {  
  37.                     if (key.isAcceptable()) {  
  38.                         SocketChannel sc = ssc.accept();  
  39.                         print("有新的连接!地址:" + sc.socket().getRemoteSocketAddress());  
  40.                         sc.configureBlocking(false);  
  41.                         sc.register(selector, SelectionKey.OP_READ);  
  42.                         // 不要写成:  
  43.                         // sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);  
  44.                         // 毕竟这样多注册的无用的事件SelectionKey.OP_WRTE  
  45.                         // 如果是这样,在完成accept后,CPU也许会跑到100%  
  46.                           
  47.                     }  
  48.                     //same to if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ) {  
  49.                     if (key.isReadable()) {   
  50.                         SocketChannel sc = (SocketChannel)key.channel();  
  51.                         print("有新的读取!地址:" + sc.socket().getRemoteSocketAddress());                        
  52.                         buffer.clear();                       
  53.                         sc.read(buffer);  
  54.                         buffer.flip();  
  55.                         byte[] b = new byte[buffer.limit()];  
  56.                         buffer.get(b);  
  57.                         String s = new String(b);  
  58.                         if (s.equals("bye")) {  
  59.                             print("断开连接:" + sc.socket().getRemoteSocketAddress());    
  60.                             //断开连接后,取消此键的通道到其选择器的注册  
  61.                             key.cancel();  
  62.                             sc.close();  
  63.                             continue;  
  64.                         }  
  65.                         print("读取的内容为:" + s);     
  66.                         buffer.clear();  
  67.                         s = "echo: " + s;  
  68.                         buffer.put(s.getBytes());  
  69.                         buffer.flip();  
  70.                         sc.write(buffer);  
  71.                     }   
  72.                 }  
  73.                 selectionKeys.clear();  
  74.             }  
  75.         } catch(IOException e) {  
  76.             e.printStackTrace();  
  77.         }   
  78.     }  
  79.       
  80.     private static void print(String s) {  
  81.         System.out.println(s);  
  82.     }  

EchoClient.java

  1. package edu.dlut.zxf.nio;  
  2.  
  3. import java.util.Set;  
  4. import java.io.BufferedReader;  
  5. import java.io.IOException;  
  6. import java.io.InputStreamReader;  
  7. import java.net.InetSocketAddress;  
  8. import java.net.InetAddress;  
  9. import java.nio.ByteBuffer;  
  10. import java.nio.channels.SelectionKey;  
  11. import java.nio.channels.Selector;  
  12. import java.nio.channels.SocketChannel;  
  13.  
  14. /**  
  15.  * Echo客户端  
  16.  * @author finux  
  17.  */ 
  18. public class EchoClient {  
  19.     public static void main(String[] args) {  
  20.         ByteBuffer buffer = ByteBuffer.allocate(EchoServer.BUFFER_SIZE);  
  21.         Selector selector = null;  
  22.         SocketChannel sc = null;  
  23.         try {  
  24.             selector = Selector.open();  
  25.             sc = SocketChannel.open();  
  26.             sc.configureBlocking(false);  
  27.             sc.connect(new InetSocketAddress(InetAddress.getByName(EchoServer.HOST), EchoServer.PORT));  
  28.             print("客户端启动,准备连接...");  
  29.             if (sc.isConnectionPending()) {  
  30.                 sc.finishConnect();  
  31.             }  
  32.             print("完成连接");  
  33.             sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);  
  34.               
  35.             boolean writed = false;  
  36.             boolean down = false;  
  37.             while (!down && selector.select() > 0) {                  
  38.                 Set<SelectionKey> selectionKeys = selector.selectedKeys();  
  39.                 for (SelectionKey key: selectionKeys) {                   
  40.                     //int ops = key.readyOps();  
  41.                     //if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE && !writed) {  
  42.                     if (key.isWritable() && !writed) {  
  43.                         System.out.print("Input(bye to end): ");  
  44.                         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));   
  45.                         String s = br.readLine();  
  46.                         if (s != null && !s.trim().equals("")) {  
  47.                             buffer.clear();  
  48.                             buffer.put(s.getBytes());  
  49.                             buffer.flip();  
  50.                             sc.write(buffer);  
  51.                             writed = true;  
  52.                             if (s.equals("bye")) {  
  53.                                 down = true;  
  54.                                 break;  
  55.                             }  
  56.                         }  
  57.                     }  
  58.                     //if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ && writed) {  
  59.                     if (key.isReadable() && writed) {  
  60.                         buffer.clear();  
  61.                         sc.read(buffer);  
  62.                         buffer.flip();  
  63.                         byte[] b = new byte[buffer.limit()];  
  64.                         buffer.get(b);  
  65.                         print(new String(b));  
  66.                         writed = false;  
  67.                     }  
  68.                 }  
  69.                 selectionKeys.clear();  
  70.             }  
  71.         } catch(IOException e) {  
  72.             e.printStackTrace();  
  73.         }  
  74.     }  
  75.       
  76.     private static void print(String s) {  
  77.         System.out.println(s);  
  78.     }  

当然EchoClient也可以像下面这样来实现:

EchoClient2.java

  1. package edu.dlut.zxf.nio;  
  2.  
  3. import java.util.Set;  
  4. import java.io.BufferedReader;  
  5. import java.io.IOException;  
  6. import java.io.InputStreamReader;  
  7. import java.net.InetSocketAddress;  
  8. import java.net.InetAddress;  
  9. import java.nio.ByteBuffer;  
  10. import java.nio.channels.SelectionKey;  
  11. import java.nio.channels.Selector;  
  12. import java.nio.channels.SocketChannel;  
  13.  
  14. /**  
  15.  * Echo客户端2  
  16.  * @author finux  
  17.  */ 
  18. public class EchoClient2 {  
  19.     public static void main(String[] args) {  
  20.         ByteBuffer buffer = ByteBuffer.allocate(EchoServer.BUFFER_SIZE);  
  21.         Selector selector = null;  
  22.         SocketChannel sc = null;  
  23.         try {  
  24.             selector = Selector.open();  
  25.             sc = SocketChannel.open();  
  26.             sc.configureBlocking(false);  
  27.             sc.register(selector, SelectionKey.OP_CONNECT);  
  28.             sc.connect(new InetSocketAddress(InetAddress.getByName(EchoServer.HOST), EchoServer.PORT));  
  29.             print("客户端启动,准备连接...");  
  30.               
  31.             boolean writed = false;  
  32.             boolean down = false;  
  33.             while (!down && selector.select() > 0) {                  
  34.                 Set<SelectionKey> selectionKeys = selector.selectedKeys();  
  35.                 for (SelectionKey key: selectionKeys) {                   
  36.                     //int ops = key.readyOps();  
  37.                     //if ((ops & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT) {  
  38.                     if (key.isConnectable()) {  
  39.                         print("完成连接!");  
  40.                         if (sc.isConnectionPending()) {  
  41.                             sc.finishConnect();  
  42.                         }  
  43.                         sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);                  
  44.                     }  
  45.                     //if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE && !writed) {  
  46.                     if (key.isWritable() && !writed) {  
  47.                         //从准备IO中读取内容  
  48.                         System.out.print("Input(bye to end): ");  
  49.                         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));   
  50.                         String s = br.readLine();  
  51.                         if (s != null && !s.trim().equals("")) {  
  52.                             buffer.clear();  
  53.                             buffer.put(s.getBytes());  
  54.                             buffer.flip();  
  55.                             sc.write(buffer);  
  56.                             writed = true;  
  57.                             if (s.equals("bye")) {  
  58.                                 down = true;  
  59.                                 break;  
  60.                             }  
  61.                         }  
  62.                     }  
  63.                     //if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ && writed) {  
  64.                     if (key.isReadable() && writed) {  
  65.                         buffer.clear();  
  66.                         sc.read(buffer);  
  67.                         buffer.flip();  
  68.                         byte[] b = new byte[buffer.limit()];  
  69.                         buffer.get(b);  
  70.                         print(new String(b));  
  71.                         writed = false;  
  72.                     }  
  73.                 }  
  74.                 selectionKeys.clear();  
  75.             }  
  76.         } catch(IOException e) {  
  77.             e.printStackTrace();  
  78.         }  
  79.     }  
  80.       
  81.     private static void print(String s) {  
  82.         System.out.println(s);  
  83.     }  

但是这样的话,显然EchoClient2中的while循环中的for循环(若有n次),在每次循环中都会多出n-1次if判断,就是下面这个:

  1. if (key.isConnectable()) { 

所以,我个人更喜欢***个EchoClient,呵呵,不用注册SelectionKey.OP_CONNECT监听事件。呵呵...

原文链接:http://finux.iteye.com/blog/368955

【编辑推荐】

  1. Java NIO 深入研究
  2. Java NIO聊天窗口实例
  3. Java NIO 经典实例代码
  4. Java NIO性能测试
  5. Java NIO TCP编程
【责任编辑:小林 TEL:(010)68476606】

点赞 0

本文来自:51CTO.COM

感谢作者:51CTO.COM

查看原文:用nio实现Echo服务

102 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传