Nivelle 开拓视野冲破艰险看见世界 身临其境贴近彼此感受生活

自己实现HTTP

2016-09-16


  1. 创建ServerSocketChannel 并设置相应参数

  2. 创建Selector 并注册到ServerSocketChannel 上

  3. 调用Selecto的select方法等待请求

  4. selector接收到请求后使用selectKeys返回SelectionKey集合

  5. 使用selectionkey 获取到channel、selector 和操作类型并进行具体操作。

public class HttpSever {

  public static void main(String args[]) throws Exception {
    // 创建ServerSocketChannel,监听8080端口
    ServerSocketChannel ssc = ServerSocketChannel.open();
    ssc.socket().bind(new InetSocketAddress(8080));
    // 设置为非阻塞模式
    ssc.configureBlocking(false);
    // 为ssc注册选择器
    Selector selector = Selector.open();
    ssc.register(selector, SelectionKey.OP_ACCEPT);

    // 创建处理器
    while (true) {
      // 等待请求,每次阻塞3s,超过3s后继续向下执行,如果传入0或者不传入参数一直阻塞
      if (selector.select(3000) == 0) {
        continue;
      }
      // 获取待处理的SelectionKey
      Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();

      while (keyIter.hasNext()) {
        SelectionKey key = keyIter.next();
        // 启动新线程处理SelectionKey
        new Thread(new HttpHandler(key)).run();
        // 处理完后,从待处理的SelectionKey迭代器中移除当前使用的key
        keyIter.remove();
      }

    }

  }

  public static class HttpHandler implements Runnable {

    private int bufferSize = 1024;
    private String localCharset = "UTF-8";
    private SelectionKey key;

    public HttpHandler(SelectionKey key) {
      this.key = key;
    }

    public void handleAccept() throws IOException {
      SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
      clientChannel.configureBlocking(false);
      clientChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufferSize));
    }

    public void handleRead() throws IOException {
      // 获取channel
      SocketChannel sc = (SocketChannel) key.channel();
      // 获取buffer并重置
      ByteBuffer buffer = (ByteBuffer) key.attachment();
      buffer.clear();
      // 没有读到内容则关闭
      if (sc.read(buffer) == -1) {
        sc.close();
      } else {
        // 接受请求数据
        buffer.flip();
        String receivedString = Charset.forName(localCharset).newDecoder().decode(buffer)
            .toString();
        // 控制台打印请求报文头
        String[] requestMessage = receivedString.split("\r\n");
        for (String s : requestMessage) {
          // 遇到空行说明报文头已经打印完
          if (s.isEmpty())
            break;
        }

        // 控制台打印首行信息
        String[] fistLine = requestMessage[0].split(" ");
        System.out.println();
        System.out.println("method:\t" + fistLine[0]);
        System.out.println("url\t" + fistLine[1]);
        System.out.println("httpServer version\t" + fistLine[2]);
        System.out.println();

        // 返回客户端
        StringBuilder sendString = new StringBuilder();
        sendString.append("HTTP/1.1 200 ok\r\n");
        sendString.append("content-type:text/thml;charset=" + localCharset + "\r\n");
        sendString.append("\r\n");
        sendString.append("<html><head><title> 显示报文</title></head></html>");
        sendString.append("接收到的报文是:<br/>");
        for (String s : requestMessage) {
          sendString.append(s + "<br/>");
        }
        sendString.append("</body></html>");
        buffer = ByteBuffer.wrap(sendString.toString().getBytes(localCharset));
        sc.write(buffer);
        sc.close();
      }
    }

    @Override
    public void run() {
      try {
        // 接收到连接请求时
        if (key.isAcceptable()) {
          handleAccept();
        }
        // 读数据
        if (key.isReadable()) {
          handleRead();
        }
      } catch (IOException ex) {
        ex.printStackTrace();
      }
    }
  }
}


总结至:《看透springMVC源代码分析与实践》 作者:韩路彪


评论