NIO-结合Selector设计一个简易WebServer

来源:互联网 发布:淘宝保暖卫衣长款 编辑:程序博客网 时间:2024/06/07 01:44

参考资料
建议大家找找这种资料来了解下什么是IO,什么是NIO,NIO的理念是什么。

一点小结论

Channel、Buffer、Selector是NIO的核心API。
Channel类似旧IO的流,可读可写,读写都是面向Buffer。
这里写图片描述
Channel通常来自文件或网络:

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();……RandomAccessFile randomAccessFile = new RandomAccessFile("/Users/zhengwei/lanqiao/ConditionOperator.java", "rw");FileChannel channel = randomAccessFile.getChannel();

Selector是非阻塞的核心,不像以前所有的读写都要阻塞,如果要并发就必须开多线程来监听每一路的IO。Selector有一种注册机制,首先读写事件不阻塞,只需向Selector注册感兴趣的事件,这样CPU可以去干点别的事,只需定期轮询Selector即可。
这里写图片描述

简易Server的实现思路

打开Server通道,并做初始化initServerChannel
实例化一个Selector,对Server通道的连接就绪事件感兴趣:

//打开一个选择器      Selector selector = Selector.open();// Selector的创建      serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 对channel的连接就绪感兴趣

轮询Selector状态,如果出现连接事件,实例化一个客户端套接字连接通道,将这个通道上的可读事件注册给Selector;

if (key.isAcceptable()) {    dealAcceptable(serverSocketChannel, selector);}

如果出现可读事件,读取客户端的HTTP连接报文,写回一段HTTP响应报文;

 else if (key.isReadable()) {// 有读的事儿没?    dealReadable(selector, key); }

如果出现可写事件,完成内容写,并关闭连接。

else if (key.isWritable()) {    dealWritable(key);}

完整代码

package org.lanqiao.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.net.ServerSocket;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.HashSet;import java.util.Iterator;import java.util.Set;public class Server implements Runnable {  //Server启停标志  private boolean interrupted = false;  public void run() {    try {      ServerSocketChannel serverSocketChannel = initServerChannel();      //打开一个选择器      Selector selector = Selector.open();// Selector的创建      serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 对channel的连接就绪感兴趣      while (!interrupted) {        //查询就绪的通道数量,这是一个阻塞方法        int readyChannels = selector.select(500);        //没有就绪的则继续进行循环        if (readyChannels == 0)          continue;        final Set<SelectionKey> selectionKeys = selector.selectedKeys();// 啥事,阻塞        //迭代当前已发生的事件        Iterator<SelectionKey> keyIterator = selectionKeys.iterator();        while (keyIterator.hasNext()) {          SelectionKey key = keyIterator.next();          if (key.isAcceptable()) {            dealAcceptable(serverSocketChannel, selector);          } else if (key.isConnectable()) {            // a connection was established with a remote server.          } else if (key.isReadable()) {// 有读的事儿没?            dealReadable(selector, key);          } else if (key.isWritable()) {            dealWritable(key);          }          //从key集合中删除key,这一步很重要          keyIterator.remove();        }      }    } catch (IOException e) {      e.printStackTrace();    }  }  private void dealWritable(SelectionKey key) throws IOException {    // a channel is ready for writing    SocketChannel clientChannel = (SocketChannel) key.channel();    clientChannel.shutdownInput();    clientChannel.close();  }  private void dealReadable(Selector selector, SelectionKey key) {    // a channel is ready for reading    //该key有Read事件    SocketChannel clientChannel = (SocketChannel) key.channel();    String lines = NIOx.readAllLine(clientChannel);    System.out.println("lines===\n" + lines);    try {      clientChannel.register(selector, SelectionKey.OP_WRITE);      //书写报文      clientChannel.write(ByteBuffer.wrap((          "HTTP/1.1 200 OK\n" +              "Date: Sat, 4 Nov 2017 23:59:59 GMT\n" +              "Content-Type: text/html;charset=utf-8\n" +              "Content-Length: 5\n" +              "\n" +              "hello").getBytes()));    } catch (IOException e) {      e.printStackTrace();    }  }  private void dealAcceptable(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {    //该key有ACCEPT事件    //将监听得到的channel强转为ServerSocketChannel    // a connection was accepted by a ServerSocketChannel.    //得到接收到的SocketChannel    SocketChannel clientChannel = serverSocketChannel.accept();    clientChannel.configureBlocking(false);// 打死不阻塞    clientChannel.register(selector, SelectionKey.OP_READ);  }  private ServerSocketChannel initServerChannel() throws IOException {    //打开ServerSocketChannel通道    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();    //得到ServerSocket对象    final ServerSocket serverSocket = serverSocketChannel.socket();    //监听端口    serverSocket.bind(new InetSocketAddress(8082));    //配置为非阻塞    serverSocketChannel.configureBlocking(false);    return serverSocketChannel;  }  public static void main(String[] args) {    new Thread(new Server()).start();  }}
原创粉丝点击