码迷,mamicode.com
首页 > Web开发 > 详细

使用NIO建立简单的http服务器

时间:2017-10-22 21:32:41      阅读:248      评论:0      收藏:0      [点我收藏+]

标签:channels   关闭   exception   byte   res   span   code   class   非阻塞   

NIO同BIO的区别在于NIO的所有操作都可以是非阻塞的,这里尝试将之前用BIO实现的htp服务器改造为用NIO实现,在改造过程中碰到不少问题,只能说知易行难

  1 import java.io.IOException;
  2 import java.net.InetSocketAddress;
  3 import java.nio.ByteBuffer;
  4 import java.nio.CharBuffer;
  5 import java.nio.channels.SelectionKey;
  6 import java.nio.channels.Selector;
  7 import java.nio.channels.ServerSocketChannel;
  8 import java.nio.channels.SocketChannel;
  9 import java.nio.charset.Charset;
 10 import java.util.Iterator;
 11 
 12 public class NioServer {
 13     private String ip;
 14 
 15     private int port;
 16 
 17     private Selector selector;
 18 
 19     public NioServer(String ip, int port) {
 20         this.ip = ip;
 21         this.port = port;
 22     }
 23 
 24     public void startListen() throws IOException {
 25         selector = Selector.open();
 26         ServerSocketChannel serverChannel = ServerSocketChannel.open();
 27         serverChannel.configureBlocking(false);
 28         serverChannel.register(selector, SelectionKey.OP_ACCEPT);
 29         serverChannel.bind(new InetSocketAddress(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<SelectionKey> 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) throws IOException {
 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) throws IOException {
 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) throws IOException {
 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     private String getResponseText() {
110         StringBuffer sb = new StringBuffer();
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("<html>");
115         sb.append("  <head>");
116         sb.append("    <title>");
117         sb.append("      NIO Http Server");
118         sb.append("    </title>");
119         sb.append("  </head>");
120         sb.append("  <body>");
121         sb.append("    <h1>Hello World!</h1>");
122         sb.append("  </body>");
123         sb.append("</html>");
124         
125         return sb.toString();
126     }
127     
128     public static void main(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 }

这里是将对请求的接受(accept)也使用Selector处理了,在该场景下,在处理完SelectioKey后一定要remove,否则会导致后面的请求不响应。在jetty源码及其它示例中,Selector只处理了对请求的读以及响应的写,请求接受并没有使用selector,这种场景下可以不remove(仅不出现后续请求不响应的问题,不代表没有其它问题):

 1         while (true) {
 2             SocketChannel channel = serverChannel.accept();
 3             if(channel != null) {
 4                 channel.configureBlocking(false);
 5                 channel.register(selector, SelectionKey.OP_READ);
 6             }
 7             
 8             //不能使用select方法,该方法会阻塞,如果在阻塞过程中channel状态就绪,会因此处阻塞而无法执行。
 9             //所以,如果调用阻塞方法,下面对channel状态的处理得另起一个常驻线程
10             int result = selector.selectNow();
11             if (result == 0) {
12                 continue;
13             }
14 
15             Iterator<SelectionKey> it = selector.selectedKeys().iterator();
16             while (it.hasNext()) {
17                 SelectionKey key = it.next();
18                 if (key.isReadable()) {
19                     //读取请求中的数据
20                     read(key);
21                 } else if (key.isWritable()) {
22                     //写响应数据
23                     write(key);
24                 } else {
25                     System.out.println("Unknow selector type: " + key.readyOps());
26                 }
27             }
28         }

 

使用NIO建立简单的http服务器

标签:channels   关闭   exception   byte   res   span   code   class   非阻塞   

原文地址:http://www.cnblogs.com/smart-elf/p/7710870.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!