用nio實(shí)現(xiàn)Echo服務(wù)
今天突然間想用nio實(shí)現(xiàn)個(gè)Echo服務(wù),程序?qū)崿F(xiàn)起來(lái)實(shí)現(xiàn)不算困難,但跑起來(lái)后,在Server端的ServerSocket完成accept之后,我的CPU總是跳到100%。嗯,小郁悶,后來(lái),才發(fā)現(xiàn)自己在Server端注冊(cè)了多余的監(jiān)聽事件SelectionKey.OP_WRITE,改過(guò)來(lái)后好多了,希望記住這個(gè)教訓(xùn)。
EchoServer.java
- 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服務(wù)器
- * @author finux
- */
- public class EchoServer {
- public final static int BUFFER_SIZE = 1024; //默認(rèn)端口
- public final static String HOST = "210.30.107.17";
- public final static int PORT = 8888;
- public static void main(String[] args) {
- ServerSocketChannel ssc = null;
- //緩沖區(qū)
- 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("服務(wù)器啟動(dòng),準(zhǔn)備好連接...");
- 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);
- // 畢竟這樣多注冊(cè)的無(wú)用的事件SelectionKey.OP_WRTE
- // 如果是這樣,在完成accept后,CPU也許會(huì)跑到100%
- }
- //same to if ((ops & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
- if (key.isReadable()) {
- SocketChannel sc = (SocketChannel)key.channel();
- print("有新的讀?。〉刂罚?quot; + 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());
- //斷開連接后,取消此鍵的通道到其選擇器的注冊(cè)
- key.cancel();
- sc.close();
- continue;
- }
- print("讀取的內(nèi)容為:" + 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
- 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("客戶端啟動(dòng),準(zhǔn)備連接...");
- 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);
- }
- }
當(dāng)然EchoClient也可以像下面這樣來(lái)實(shí)現(xiàn):
EchoClient2.java
- 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("客戶端啟動(dòng),準(zhǔn)備連接...");
- 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) {
- //從準(zhǔn)備IO中讀取內(nèi)容
- 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循環(huán)中的for循環(huán)(若有n次),在每次循環(huán)中都會(huì)多出n-1次if判斷,就是下面這個(gè):
- if (key.isConnectable()) {
所以,我個(gè)人更喜歡***個(gè)EchoClient,呵呵,不用注冊(cè)SelectionKey.OP_CONNECT監(jiān)聽事件。呵呵...
原文鏈接:http://finux.iteye.com/blog/368955
【編輯推薦】