用nio实现Echo服务--代码

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

 

EchoServer.java

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.     }  
  83. }  
package edu.dlut.zxf.nio;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Set;

/**
 * Echo服务器
 * @author finux
 */
public class EchoServer {
	public final static int BUFFER_SIZE = 1024; //默认端口
	public final static String HOST = "210.30.107.17";
	public final static int PORT = 8888;
	
	public static void main(String[] args) {
		ServerSocketChannel ssc = null;
		//缓冲区
		ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
		Selector selector = null;
		try {
			selector = Selector.open();
			ssc = ServerSocketChannel.open();
			ssc.socket().bind(new InetSocketAddress(InetAddress.getByName(HOST), PORT));
			ssc.configureBlocking(false);
			ssc.register(selector, SelectionKey.OP_ACCEPT);		
			print("服务器启动,准备好连接...");
			while (selector.select() > 0) {		
				Set<SelectionKey> selectionKeys = selector.selectedKeys();
				for (SelectionKey key: selectionKeys) {
					if (key.isAcceptable()) {
						SocketChannel sc = ssc.accept();
						print("有新的连接!地址:" + sc.socket().getRemoteSocketAddress());
						sc.configureBlocking(false);
						sc.register(selector, SelectionKey.OP_READ);
						// 不要写成:
						// sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
						// 毕竟这样多注册的无用的事件SelectionKey.OP_WRTE
						// 如果是这样,在完成accept后,CPU也许会跑到100%
						
					}
					//same to if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
					if (key.isReadable()) { 
						SocketChannel sc = (SocketChannel)key.channel();
						print("有新的读取!地址:" + sc.socket().getRemoteSocketAddress());						
						buffer.clear();						
						sc.read(buffer);
						buffer.flip();
						byte[] b = new byte[buffer.limit()];
						buffer.get(b);
						String s = new String(b);
						if (s.equals("bye")) {
							print("断开连接:" + sc.socket().getRemoteSocketAddress());	
							//断开连接后,取消此键的通道到其选择器的注册
							key.cancel();
							sc.close();
							continue;
						}
						print("读取的内容为:" + s);	
						buffer.clear();
						s = "echo: " + s;
						buffer.put(s.getBytes());
						buffer.flip();
						sc.write(buffer);
					} 
				}
				selectionKeys.clear();
			}
		} catch(IOException e) {
			e.printStackTrace();
		} 
	}
	
	private static void print(String s) {
		System.out.println(s);
	}
}

 

EchoClient.java

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.     }  
  79. }  
package edu.dlut.zxf.nio;

import java.util.Set;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;

/**
 * Echo客户端
 * @author finux
 */
public class EchoClient {
	public static void main(String[] args) {
		ByteBuffer buffer = ByteBuffer.allocate(EchoServer.BUFFER_SIZE);
		Selector selector = null;
		SocketChannel sc = null;
		try {
			selector = Selector.open();
			sc = SocketChannel.open();
			sc.configureBlocking(false);
			sc.connect(new InetSocketAddress(InetAddress.getByName(EchoServer.HOST), EchoServer.PORT));
			print("客户端启动,准备连接...");
			if (sc.isConnectionPending()) {
				sc.finishConnect();
			}
			print("完成连接");
			sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
			
			boolean writed = false;
			boolean down = false;
			while (!down && selector.select() > 0) {				
				Set<SelectionKey> selectionKeys = selector.selectedKeys();
				for (SelectionKey key: selectionKeys) {					
					//int ops = key.readyOps();
					//if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE && !writed) {
					if (key.isWritable() && !writed) {
						System.out.print("Input(bye to end): ");
						BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
						String s = br.readLine();
						if (s != null && !s.trim().equals("")) {
							buffer.clear();
							buffer.put(s.getBytes());
							buffer.flip();
							sc.write(buffer);
							writed = true;
							if (s.equals("bye")) {
								down = true;
								break;
							}
						}
					}
					//if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ && writed) {
					if (key.isReadable() && writed) {
						buffer.clear();
						sc.read(buffer);
						buffer.flip();
						byte[] b = new byte[buffer.limit()];
						buffer.get(b);
						print(new String(b));
						writed = false;
					}
				}
				selectionKeys.clear();
			}
		} catch(IOException e) {
			e.printStackTrace();
		}
	}
	
	private static void print(String s) {
		System.out.println(s);
	}
}

 

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

EchoClient2.java

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.     }  
  84. }  
package edu.dlut.zxf.nio;

import java.util.Set;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;

/**
 * Echo客户端2
 * @author finux
 */
public class EchoClient2 {
	public static void main(String[] args) {
		ByteBuffer buffer = ByteBuffer.allocate(EchoServer.BUFFER_SIZE);
		Selector selector = null;
		SocketChannel sc = null;
		try {
			selector = Selector.open();
			sc = SocketChannel.open();
			sc.configureBlocking(false);
			sc.register(selector, SelectionKey.OP_CONNECT);
			sc.connect(new InetSocketAddress(InetAddress.getByName(EchoServer.HOST), EchoServer.PORT));
			print("客户端启动,准备连接...");
			
			boolean writed = false;
			boolean down = false;
			while (!down && selector.select() > 0) {				
				Set<SelectionKey> selectionKeys = selector.selectedKeys();
				for (SelectionKey key: selectionKeys) {					
					//int ops = key.readyOps();
					//if ((ops & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT) {
					if (key.isConnectable()) {
						print("完成连接!");
						if (sc.isConnectionPending()) {
							sc.finishConnect();
						}
						sc.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);				
					}
					//if ((ops & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE && !writed) {
					if (key.isWritable() && !writed) {
						//从准备IO中读取内容
						System.out.print("Input(bye to end): ");
						BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
						String s = br.readLine();
						if (s != null && !s.trim().equals("")) {
							buffer.clear();
							buffer.put(s.getBytes());
							buffer.flip();
							sc.write(buffer);
							writed = true;
							if (s.equals("bye")) {
								down = true;
								break;
							}
						}
					}
					//if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ && writed) {
					if (key.isReadable() && writed) {
						buffer.clear();
						sc.read(buffer);
						buffer.flip();
						byte[] b = new byte[buffer.limit()];
						buffer.get(b);
						print(new String(b));
						writed = false;
					}
				}
				selectionKeys.clear();
			}
		} catch(IOException e) {
			e.printStackTrace();
		}
	}
	
	private static void print(String s) {
		System.out.println(s);
	}
}

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

Java代码 复制代码  收藏代码
  1. if (key.isConnectable()) {  
if (key.isConnectable()) {

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

你可能感兴趣的:(echo)