Java 网络编程 —— 创建非阻塞的 HTTP 服务器

打印 上一主题 下一主题

主题 893|帖子 893|积分 2679

HTTP 概述

HTTP 客户程序必须先发出一个 HTTP 请求,然后才能接收到来自 HTTP 服器的响应,浏览器就是最常见的 HTTP 客户程序。HTTP 客户程序和 HTTP 服务器分别由不同的软件开发商提供,它们都可以用任意的编程语言编写。HTTP 严格规定了 HTTP 请求和 HTTP 响应的数据格式,只要 HTTP 服务器与客户程序都遵守 HTTP,就能彼此看得懂对方发送的消息
1. HTTP 请求格式

下面是一个 HTTP 请求的例子
  1. POST /hello.jsp HTTP/1.1
  2. Accept:image/gif, image/jpeg, */*
  3. Referer: http://localhost/login.htm
  4. Accept-Language: en,zh-cn;q=0.5
  5. Content-Type: application/x-www-form-urlencoded
  6. Accept-Encoding: gzip, deflate
  7. User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 10.0)
  8. Host: localhost
  9. Content-Length:43
  10. Connection: Keep-Alive
  11. Cache-Control: no-cache
  12. username=root&password=12346&submit=submit
复制代码
HTTP 规定,HTTP 请求由三部分构成,分别是:

  • 请求方法、URI、HTTP 的版本

    • HTTP 请求的第一行包括请求方式、URI 和协议版本这三项内容,以空格分开:POST /hello.jsp HTTP/1.1

  • 请求头(Request Header)

    • 请求头包含许多有关客户端环境和请求正文的有用信息。例如,请求头可以声明浏览器的类型、所用的语言、请求正文的类型,以及请求正文的长度等
      1. Accept:image/gif, image/jpeg, */*
      2. Referer: http://localhost/login.htm
      3. Accept-Language: en,zh-cn;q=0.5                //浏览器所用的语言
      4. Content-Type: application/x-www-form-urlencoded                //正文类型
      5. Accept-Encoding: gzip, deflate
      6. User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 10.0)        //浏览器类型
      7. Host: localhost         //远程主机
      8. Content-Length:43        //正文长度
      9. Connection: Keep-Alive
      10. Cache-Control: no-cache
      复制代码

  • 请求正文(Request Content)

    • HTTP 规定,请求头和请求正文之间必须以空行分割(即只有 CRLF 符号的行),这个空行非常重要,它表示请求头已经结束,接下来是请求正文,请求正文中可以包含客户以 POST 方式提交的表单数据
      1. username=root&password=12346&submit=submit
      复制代码

2. HTTP 响应格式

下面是一个 HTTP 响应的例子
  1. HTTP/1.1 200 0K
  2. Server: nio/1.1
  3. Content-type: text/html; charset=GBK
  4. Content-length:97
  5.    
  6. <html>
  7. <head>
  8.         <title>helloapp</title>
  9. </head>
  10. <body >
  11.         <h1>hello</h1>
  12. </body>
  13. </htm1>
复制代码
HTTP 响应也由三部分构成,分别是:

  • HTTP 的版本、状态代码、描述

    • HTTP 响应的第一行包括服务器使用的 HTTP 的版本、状态代码,以及对状态代码的描述,这三项内容之间以空格分割

  • 响应头 (Response Header)

    • 响应头也和请求头一样包含许多有用的信息,例如服务器类型、正文类型和正文长度等
      1. Server: nio/1.1                //服务器类型
      2. Content-type: text/html; charset=GBK        //正文类型
      3. Content-length:97        //正文长度
      复制代码

  • 响应正文(Response Content)

    • 响应正文就是服务器返回的具体的文档,最常见的是 HTML 网页。HTTP 响应头与响应正文之间也必须用空行分隔
      1. <html>
      2. <head>
      3.         <title>helloapp</title>
      4. </head>
      5. <body >
      6.         <h1>hello</h1>
      7. </body>
      8. </htm1>
      复制代码


创建阻塞的 HTTP 服务器

下例(SimpleHttpServer)创建了一个非常简单的 HTTP 服务器,它接收客户程序的 HTTP 请求,把它打印到控制台。然后对 HTTP 请求做简单的解析,如果客户程序请求访问 login.htm,就返回该网页,否则一律返回 hello.htm 网页。login.htm 和 hello.htm 文件位于 root 目录下
SimpleHttpServer 监听 80 端口,按照阻塞模式工作,采用线程池来处理每个客户请求
  1. public class SimpleHttpServer {
  2.    
  3.     private int port = 80;
  4.     private ServerSocketChannel serverSocketChannel = null;
  5.     private ExecutorService executorService;
  6.     private static final int POOL MULTIPLE = 4;
  7.     private Charset charset = Charset.forName("GBK");
  8.    
  9.     public SimpleHttpServer() throws IOException {
  10.         executorService= Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * POOL MULTIPLE);
  11.         serverSocketChannel = ServerSocketChannel.open();
  12.         serverSocketChannel.socket().setReuseAddress(true);
  13.         serverSocketChannel.socket().bind(new InetSocketAddress(port));
  14.         System.out.println("服务器启动");
  15.     }
  16.    
  17.     public void service() {
  18.         while (true) {
  19.             SocketChannel socketChannel = null;
  20.             try {
  21.                 socketChannel = serverSocketChannel.accept();
  22.                 executorService.execute(new Handler(socketChannel));
  23.             } catch (IOException e) {
  24.                 e.printStackTrace();
  25.             }
  26.         }
  27.     }
  28.    
  29.     public static void main(String args[])throws IOException {
  30.         new SimpleHttpServer().service();
  31.     }
  32.    
  33.     public String decode(ByteBuffer buffer) {......}        //解码
  34.    
  35.     public ByteBuffer encode(String str) {......}        //编码
  36.    
  37.     //Handler是内部类,负责处理HTTP请求
  38.     class Handler implements Runnable {
  39.         
  40.         private SocketChannel socketChannel;
  41.         
  42.         public Handler(SocketChannel socketChannel) {
  43.             this.socketChannel = socketChannel;
  44.         }
  45.         
  46.         public void run() {
  47.             handle(socketChannel);
  48.         }
  49.         
  50.         public void handle(SocketChannel socketChannel) {
  51.             try {
  52.                 Socket socket = socketChannel.socket();
  53.                 ByteBuffer buffer = ByteBuffer.allocate(1024);
  54.                
  55.                 //接收HTTP请求,假定其长度不超过1024字节
  56.                 socketChannel.read(buffer);
  57.                 buffer.flip();
  58.                 String request = decode(buffer);
  59.                 //打印HTTP请求
  60.                 System.out.print(request);
  61.                
  62.                 //生成HTTP响应结果
  63.                 StringBuffer sb = new StringBuffer("HTTP/1.1 200 0K\r\n");
  64.                 sb.append("Content-Type:text/html\r\n\r\n");
  65.                 //发送HTTP响应的第1行和响应头
  66.                 socketChannel.write(encode(sb.toString()));
  67.                
  68.                 FileInputStream in;
  69.                 //获得HTTP请求的第1行
  70.                 String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
  71.                 if(firstLineOfRequest.indexOf("login.htm") != -1) {
  72.                     in = new FileInputStream("login.htm");
  73.                 } else {
  74.                     in = new FileInputStream("hello.htm");
  75.                 }
  76.                     
  77.                 FileChannel fileChannel = in.getChannel();
  78.                 //发送响应正文
  79.                 fileChannel.transferTo(0, fileChannel.size(), socketChannel);
  80.             } catch (Exception e) {
  81.                 e.printStackTrace();
  82.             } finally {
  83.                 try {
  84.                     if(socketChannel != null) {
  85.                         //关闭连接
  86.                         socketChannel.close();
  87.                     }
  88.                 } catch (IOException e) {
  89.                     e.printStackTrace();
  90.                 }
  91.             }
  92.         }
  93.     }
  94. }
复制代码
创建非阻塞的 HTTP 服务器

下面是本节所介绍的非阻塞的 HTTP 服务器范例的模型

  • HttpServer:服务器主程序,由它启动服务器
  • AcceptHandler:负责接收客户连接
  • RequestHandler:负责接收客户的 HTTP 请求,对其解析,然后生成相应的 HTTP 响应,再把它发送给客户
  • Request:表示 HTTP 请求
  • Response:表示 HTTP 响应
  • Content:表示 HTTP 响应的正文
1. 服务器主程序 HttpServer

HttpServer 仅启用了单个主线程,采用非阻塞模式来接收客户连接,以及收发数据
  1. public class HttpServer {
  2.    
  3.     private Selector selector = null;
  4.     private ServerSocketChannel serverSocketChannel = null;
  5.     private int port = 80;
  6.     private Charset charset = Charset.forName("GBK");
  7.    
  8.     public HttpServer() throws IOException {
  9.         //创建Selector和ServerSocketChannel
  10.         //把ServerSocketchannel设置为非阻塞模式,绑定到80端口
  11.         ......
  12.     }
  13.    
  14.     public void service() throws IOException {
  15.         //注册接收连接就绪事件
  16.         serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT, new AcceptHandler());
  17.         while(true) {
  18.             int n = selector.select();
  19.             if(n==0) continue;
  20.             Set readyKeys = selector.selectedKeys();
  21.             Iterator it = readyKeys.iterator();
  22.                         while(it.hasNext()) {
  23.                 SelectionKey key = null;
  24.                 try {
  25.                     key = (SelectionKey) it.next();
  26.                     it.remove();
  27.                     final Handler handler = (Handler) key.attachment();
  28.                     handler.handle(key); //由 Handler 处理相关事件
  29.                 } catch(IOException e) {
  30.                     e.printStackTrace();
  31.                     try {
  32.                         if(key != null) {
  33.                             key.cancel();
  34.                             key.channel().close();
  35.                         }
  36.                     } catch(Exception ex) {
  37.                         e.printStackTrace();
  38.                     }
  39.                 }
  40.             }            
  41.         }
  42.     }
  43.    
  44.     public static void main(String args[])throws Exception {
  45.         final HttpServer server = new HttpServer();
  46.         server.service();
  47.     }
  48. }
复制代码
2. 具有自动增长的缓冲区的 ChannelIO 类

自定义的 ChannelIO 类对 SocketChannel 进行了包装,增加了自动增长缓冲区容量的功能。当调用 socketChannel.read(ByteBuffer bufer) 方法时,如果 buffer 已满,即使通道中还有未接收的数据,read 方法也不会读取任何数据,而是直接返回 0,表示读到了零字节
为了能读取通道中的所有数据,必须保证缓冲区的容量足够大。在 ChannelIO 类中有一个 requestBuffer 变量,它用来存放客户的 HTTP 请求数据,当 requestBuffer 剩余容量已经不足 5%,并且还有 HTTP 请求数据未接收时,ChannellO 会自动扩充 requestBuffer 的容量,该功能由 resizeRequestBuffer() 方法完成
  1. public class ChannelIO {
  2.    
  3.     protected SocketChannel socketChannel;
  4.     protected ByteBuffer requestBuffer; //存放请求数据
  5.     private static int requestBufferSize = 4096;
  6.    
  7.     public ChannelIO(SocketChannel socketChannel, boolean blocking) throws IOException {
  8.         this.socketChannel = socketChannel;
  9.         socketChannel.configureBlocking(blocking); //设置模式
  10.         requestBuffer = ByteBuffer.allocate(requestBufferSize);
  11.     }
  12.    
  13.     public SocketChannel
  14.         () {
  15.         return socketChannel;
  16.     }
  17.    
  18.     /**
  19.      * 如果原缓冲区的剩余容量不够,就创建一个新的缓冲区,容量为原来的两倍
  20.      * 并把原来缓冲区的数据拷贝到新缓冲区
  21.      */
  22.     protected void resizeRequestBuffer(int remaining) {
  23.         if (requestBuffer.remaining() < remaining) {
  24.             ByteBuffer bb = ByteBuffer.allocate(requestBuffer.capacity() * 2);
  25.             requestBuffer.flip();
  26.             bb.put(requestBuffer); //把原来缓冲区中的数据拷贝到新的缓冲区
  27.             requestBuffer = bb;
  28.         }
  29.     }
  30.    
  31.     /**
  32.      * 接收数据,把它们存放到requestBuffer
  33.      * 如果requestBuffer的剩余容量不足5%
  34.      * 就通过resizeRequestBuffer()方法扩充容量
  35.      */
  36.     public int read() throws IOException {
  37.         resizeRequestBuffer(requestBufferSize/20);
  38.         return socketChannel.read(requestBuffer);
  39.     }
  40.    
  41.     /** 返回requestBuffer,它存放了请求数据 */
  42.     public ByteBuffer getReadBuf() {
  43.         return requestBuffer;
  44.     }
  45.    
  46.     /** 发送参数指定的 ByteBuffer 的数据 */
  47.     public int write(ByteBuffer src) throws IOException {
  48.         return socketChannel.write(src);
  49.     }
  50.    
  51.     /** 把FileChannel的数据写到SocketChannel */
  52.     public long transferTo(FileChannel fc, long pos, long len) throws IOException {
  53.         return fc.transferTo(pos, len, socketChannel);
  54.     }
  55.    
  56.     /** 关闭SocketChannel */
  57.     public void close() throws IOException {
  58.         socketChannel.close();
  59.     }
  60. }
复制代码
3. 负责处理各种事件的 Handler 接口

Handler 接口负责处理各种事件,它的定义如下:
  1. public interface Handler {
  2.     public void handle(SelectionKey key) throws IOException;
  3. }
复制代码
Handler 接口有 AcceptHandler 和 RequestHandler 两个实现类。AcceptHandler 负责处理接收连接就绪事件,RequestHandler 负责处理读就绪和写就绪事件。更确切地说,RequestHandler 负责接收客户的 HTTP 请求,以及发送 HTTP 响应
4. 负责处理接收连接就绪事件的 AcceptHandler类

AcceptHandler 负责处理接收连接就绪事件,获得与客户连接的 SocketChannel,然后向 Selector 注册读就绪事件,并且创建了一个 RequestHandler,把它作为 SelectionKey 的附件。当读就绪事件发生时,将由这个 RequestHandler 来处理该事件
  1. public class AcceptHandler implements Handler {
  2.    
  3.     public void handle(SelectionKey key) throws IOException {
  4.         ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
  5.         //在非阻塞模式下,serverSocketChannel.accept()有可能返回null
  6.         SocketChannel socketChannel = serverSocketChannel.accept();
  7.         if (socketChannel == null) return;
  8.         //ChannelIO设置为采用非阻塞模式
  9.         ChannelIO cio = new ChannelIO(socketChannel, false);
  10.         RequestHandler rh = new RequestHandler(cio);
  11.         //注册读就绪事件,把RequestHandler作为附件
  12.         socketChannel.register(key.selector(), SelectionKey.OP_READ, rh);
  13.     }
  14. }
复制代码
5. 负责接收 HTTP 请求和发送 HTTP 响应的 RequestHandler 类

RequestHandler 先通过 ChannelIO 来接收 HTTP 请求,当接收到 HTTP 请求的所有数据后,就对 HTTP 请求数据进行解析,创建相应的 Request 对象,然后依据客户的请求内容,创建相应的 Response 对象,最后发送 Response 对象中包含的 HTTP 响应数据。为了简化程序,RequestHandler 仅仅支持 GET 和 HEAD 两种请求方式
  1. public class RequestHandler implements Handler {
  2.    
  3.     private ChannelIO channelIO;
  4.     //存放HTTP请求的缓冲区
  5.     private ByteBuffer requestByteBuffer = null;
  6.     //表示是否已经接收到HTTP请求的所有数据
  7.     private boolean requestReceived = false;
  8.     //表示HTTP请求
  9.     private Request request = null;
  10.     //表示HTTP响应
  11.     private Response response = null;
  12.    
  13.     RequestHandler(ChannelIO channelIO) {
  14.         this.channelIO = channelIO;
  15.     }
  16.    
  17.     /** 接收HTTP请求,发送HTTP响应 */
  18.     public void handle(SelectionKey sk) throws IOException {
  19.         try {
  20.             //如果还没有接收HTTP请求的所有数据,就接收HTTP请求
  21.             if (request == null) {
  22.                 if (!receive(sk)) return;
  23.                 requestByteBuffer.flip();
  24.                 //如果成功解析了HTTP请求,就创建一个Response对象
  25.                 if (parse()) build();
  26.                 try {
  27.                     //准备HTTP响应的内容
  28.                     response.prepare();
  29.                 } catch (IOException x) {
  30.                     response.release();
  31.                     response = new Response(Response.Code.NOT_FOUND, new StringContent(x.getMessage()));
  32.                     response.prepare();
  33.                 }
  34.                
  35.                 if (send()) {
  36.                     //如果HTTP响应没有发送完毕,则需要注册写就绪事件,以便在写就绪事件发生时继续发送数据
  37.                     sk.interestOps(SelectionKey.OP_WRITE);
  38.                 } else {
  39.                     //如HTTP响应发送完毕,就断开底层连接,并且释放Response占用资源
  40.                     channelIO.close();
  41.                     response.release();
  42.                 }
  43.             } else {
  44.                 //如果已经接收到HTTP请求的所有数据
  45.                 //如果HTTP响应发送完毕
  46.                 if (!send()) {
  47.                     channelIO.close();
  48.                     response.release();
  49.                 }
  50.             }
  51.         } catch (IOException e) {
  52.             e.printStackTrace();
  53.             channelIO.close();
  54.             if (response != null) {
  55.                 response.release();
  56.             }
  57.         }
  58.     }
  59.    
  60.     /**
  61.      * 接收HTTP请求,如果已经接收到了HTTP请求的所有数据,就返回true,否则返回false
  62.      */
  63.     private boolean receive(SelectionKey sk) throws IOException {
  64.         ByteBuffer tmp = null;
  65.         //如果已经接收到HTTP请求的所有数据,就返回true
  66.         if (requestReceived) return true;
  67.         //如果已经读到通道的末尾,或者已经读到HTTP请求数据的末尾标志,就返回true
  68.         if ((channelIO.read() < 0) || Request.isComplete(channelIO.getReadBuf())) {
  69.             requestByteBuffer = channelIO.getReadBuf();
  70.             return (requestReceived = true);
  71.         }
  72.         return false;
  73.     }
  74.    
  75.     /**
  76.      * 通过Request类的parse()方法,解析requestByteBuffer的HTTP请求数据
  77.      * 构造相应的Request对象
  78.      */
  79.     private boolean parse() throws IOException {
  80.         try {
  81.             request = Request.parse(requestByteBuffer);
  82.             return true;
  83.         } catch (MalformedRequestException x) {
  84.             //如果HTTP请求的格式不正确,就发送错误信息
  85.             response = new Response(Response.Code.BAD_REQUEST, new StringContent(x))
  86.         }
  87.         return false;
  88.     }
  89.    
  90.     /** 创建HTTP响应 */
  91.     private void build() throws IOException {
  92.         Request.Action action = request.action();
  93.         //仅仅支持GET和HEAD请求方式
  94.         if ((action != Request.Action.GET) && (action != Request.Action.HEAD)) {
  95.             response = new Response(Response.Code.METHOD_NOT_ALLOWED, new StringContent("Method Not Allowed"));
  96.         } else {
  97.             response = new Response(Response.Code.OK, new FileContent(request.uri()), action);
  98.         }
  99.     }
  100.    
  101.     /** 发送HTTP响应,如果全部发送完毕,就返回false,否则返回true */
  102.     private boolean send() throws IOException {
  103.         return response.send(channelIO);
  104.     }
  105. }
复制代码
6. 代表 HTTP 请求的 Request 类

RequestHandler 通过 ChannelIO 读取 HTTP 请求数据时,这些数据被放在 requestByteBuffer 中。当 HTTP 请求的所有数据接收完毕,就要对 requestByteBufer 的数据进行解析,然后创建相应的 Request 对象。Request 对象就表示特定的 HTTP 请求
  1. public class Request {
  2.    
  3.     //枚举类,表示HTTP请求方式
  4.     static enum Action {
  5.         GET,PUT,POST,HEAD;
  6.     }
  7.    
  8.     public static Action parse(String s) {
  9.         if (s.equals("GET"))
  10.             return GET;
  11.         if (s.equals("PUT"))
  12.             return PUT;
  13.         if (s.equals("POST"))
  14.             return POST;
  15.         if (s,equals("HEAD"))
  16.             return HEAD;
  17.         throw new IllegalArgumentException(s);
  18.     }
  19.    
  20.     private Action action;        //请求方式
  21.     private String version;        //HTTP版本
  22.     private URI uri;                //URI
  23.    
  24.     public Action action() { return action; }
  25.     public String version() { return version; }
  26.     public URI uri() { return uri; }
  27.    
  28.     private Request(Action a, String V, URI u) {
  29.         action = a;
  30.         version = v;
  31.         uri =u;
  32.     }
  33.    
  34.     public String toString() {
  35.         return (action + " " + version + " " + uri);
  36.     }
  37.    
  38.     private static Charset requestCharset = Charset.forName("GBK");
  39.    
  40.     /**
  41.      * 判断ByteBuffer是否包含HTTP请求的所有数据
  42.      * HTTP请求以”r\n\r\n”结尾
  43.      */
  44.     public static boolean isComplete(ByteBuffer bb) {
  45.         ByteBuffer temp = bb.asReadOnlyBuffer();
  46.         temp.flip();
  47.         String data = requestCharset.decode(temp).toString();
  48.         if(data.indexOf("r\n\r\n") != -1) {
  49.             return true;
  50.         }
  51.         return false;
  52.     }
  53.    
  54.     /**
  55.      * 删除请求正文
  56.      */
  57.     private static ByteBuffer deleteContent (ByteBuffer bb) {
  58.         ByteBuffer temp = bb.asReadOnlyBuffer();
  59.         String data = requestCharset.decode(temp).toString();
  60.         if(data.indexOf("\r\n\r\n") != -1) {
  61.             data = data.substrinq(0, data.indexOf("\r\n\r\n") + 4);
  62.             return requestCharset.encode(data);
  63.         }
  64.         return bb;
  65.     }
  66.    
  67.     /**
  68.      * 设定用于解析HTTP请求的字符串匹配模式,对于以下形式的HTTP请求
  69.      * GET /dir/file HTTP/1.1
  70.      * Host: hostname
  71.      * 将被解析成:
  72.      * group[l] = "GET”
  73.      * group[2]="/dir/file"
  74.      * group[3]="1.1"
  75.      * group[4]="hostname"
  76.      */
  77.     private static Pattern requestPattern =
  78.         Pattern.compile("\\A([A-Z]+) +([^]+) +HTTP/([0-9\\.]+)$"
  79.                         + ",*^Host:([]+)$.*\r\n\r\n\\z",
  80.                         Pattern.MULTILINE | Pattern.DOTALL);
  81.    
  82.     /** 解析HTTP请求,创建相应的Request对象 */
  83.     public static Request parse(ByteBuffer bb) throws MalformedRequestException {
  84.         bb = deleteContent(bb); //删除请求正文
  85.         CharBuffer cb = requestCharset.decode(bb); //解码
  86.         Matcher m = requestPattern.matcher(cb); //进行字符串匹配
  87.         //如果HTTP请求与指定的字符串式不匹配,说明请求数据不正确
  88.         if (!m.matches())
  89.             throw new MalformedRequestException();
  90.         Action a;
  91.         //获得请求方式
  92.         try {
  93.             a = Action.parse(m.group(1));
  94.         } catch (IllegalArgumentException x) {
  95.             throw new MalformedRequestException();
  96.         }
  97.         //获得URI
  98.         URI u;
  99.         try {
  100.             u=new URI("http://" + m.group(4) + m.group(2));
  101.         } catch (URISyntaxException x) {
  102.             throw new MalformedRequestException();
  103.         }
  104.         //创建一个Request对象,并将其返回
  105.         return new Request(a, m.group(3), u);
  106.     }
  107. }
复制代码
7. 代表 HTTP 响应的 Response 类

Response 类表示 HTTP 响应,它有三个成员变量:code、headerBufer 和 content,它们分别表示 HTTP 响应中的状态代码、响应头和正文
[code]public class Response implements Sendable {        //枚举类,表示状态代码    static enum Code {                OK(200, "OK"),        BAD_REQUEST(400, "Bad Request"),        NOT_FOUND(404, "Not Found"),        METHOD_NOT_ALLOWED(405, "Method Not Allowed");                private int number;        private String reason;                private Code(int i, String r) {            number = i;            reason =r;        }                public String toString() {            return number + " "  + reason;        }    }        private Code code; //状态代码    private Content content; //响应正文    private boolean headersOnly; //表示HTTP响应中是否仅包含响应头    private ByteBuffer headerBuffer = null; //响应头        public Response(Code rc, Content c) {        this(rc, c, null);    }        public Response(Code rc, Content c, Request.Action head) {        code = rc;        content = c;        headersOnly = (head == Request.Action.HEAD);    }        /** 创建响应头的内容,把它存放到ByteBuffer */    private ByteBuffer headers() {        CharBuffer cb = CharBuffer.allocate(1024);        while(true) {            try {                cb.put("HTTP/1.1").put(code.toString()).put(CRLF);                cb.put("Server: nio/1.1").put(CRLF);                cb.put("Content-type: ") .put(content.type()).put(CRIE);                cb.put("Content-length: ").put(Long.toString(content.length())).put(CRLF);                cb.put(CRLF);                break;            } catch (BufferOverflowException x) {                assert(cb.capacity() < (1
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

惊雷无声

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表