1 importjava.io.IOException;2 importjava.net.InetSocketAddress;3 importjava.nio.ByteBuffer;4 importjava.nio.CharBuffer;5 importjava.nio.channels.SelectionKey;6 importjava.nio.channels.Selector;7 importjava.nio.channels.ServerSocketChannel;8 importjava.nio.channels.SocketChannel;9 importjava.nio.charset.Charset;10 importjava.util.Iterator;11
12 public classNioServer {13 privateString ip;14
15 private intport;16
17 privateSelector selector;18
19 public NioServer(String ip, intport) {20 this.ip =ip;21 this.port =port;22 }23
24 public void startListen() throwsIOException {25 selector =Selector.open();26 ServerSocketChannel serverChannel =ServerSocketChannel.open();27 serverChannel.configureBlocking(false);28 serverChannel.register(selector, SelectionKey.OP_ACCEPT);29 serverChannel.bind(newInetSocketAddress(ip, port));30
31 while (true) {32 //不能使用select方法,该方法会阻塞,如果在阻塞过程中channel状态就绪,会因此处阻塞而无法执行。33 //所以,如果调用阻塞方法,下面对channel状态的处理得另起一个常驻线程
34 int result =selector.selectNow();35 if (result == 0) {36 continue;37 }38
39 Iterator it =selector.selectedKeys().iterator();40 while(it.hasNext()) {41 SelectionKey key =it.next();42 if(key.isAcceptable()) {43 accept(key);44 } else if(key.isReadable()) {45 read(key);46 } else if(key.isWritable()) {47 write(key);48 } else{49 System.out.println("Unknow selector type");50 }51
52 //一定要调用remove方法将已经处理过的SelectionKey清除掉,否则会造成后面的请求无法接受
53 it.remove();54 }55 }56 }57
58 private void accept(SelectionKey key) throwsIOException {59 System.out.println("Receive connection");60 ServerSocketChannel serverSocketChannel =(ServerSocketChannel) key.channel();61 SocketChannel channel =serverSocketChannel.accept();62
63 if (channel != null) {64 channel.configureBlocking(false);65 channel.register(selector, SelectionKey.OP_READ);66 }67 System.out.println("Connection end");68 }69
70 private void read(SelectionKey key) throwsIOException {71 System.out.println("Start read");72 SocketChannel channel =(SocketChannel) key.channel();73 ByteBuffer buffer = ByteBuffer.allocate(64);74 boolean hasContent = false;75
76 //这里的判断条件不能是不等于-1,因为channel一直都在,只是在数据被读完后里面为空,返回的长度是0.用-1判断会无限循环无法退出
77 while (channel.read(buffer) > 0) {78 buffer.flip(); //切换为读模式
79 CharBuffer cb = Charset.forName("UTF-8").decode(buffer);80 System.out.print(cb.toString());81 buffer.clear();82 hasContent = true;83 }84
85 if(hasContent) {86 //设置interestOps,用于写响应
87 key.interestOps(SelectionKey.OP_WRITE);88 } else{89 channel.close();90 }91 System.out.println("Read end");92 }93
94 private void write(SelectionKey key) throwsIOException {95 System.out.println("Start write");96 SocketChannel channel =(SocketChannel) key.channel();97
98 String resText =getResponseText();99 ByteBuffer buffer =ByteBuffer.wrap(resText.getBytes());100
101 //此处不可使用channel.write(buffer) != -1来判断,因为在两端都不关闭的情况下,会一直返回0,导致该循环无法退出
102 while(buffer.hasRemaining()) {103 channel.write(buffer);104 }105 channel.close();106 System.out.println("End write");107 }108
109 privateString getResponseText() {110 StringBuffer sb = newStringBuffer();111 sb.append("HTTP/1.1 200 OK\n");112 sb.append("Content-Type: text/html; charset=UTF-8\n");113 sb.append("\n");114 sb.append("");115 sb.append("
");116 sb.append(" ");117 sb.append(" NIO Http Server");118 sb.append(" ");119 sb.append(" ");120 sb.append(" ");121 sb.append("Hello World!
");122 sb.append(" ");123 sb.append("");124125 returnsb.toString();126 }127
128 public static voidmain(String[] args) {129 NioServer server = new NioServer("127.0.0.1", 8080);130 try{131 server.startListen();132 } catch(IOException e) {133 e.printStackTrace();134 }135 }136
137 }