第五十三篇: JAVA简易WEB服务器(三)

来源:互联网 发布:40mac全灭 圆盘是生物 编辑:程序博客网 时间:2024/05/22 10:49

在上一篇《JAVA简易WEB服务器(二)》中我们完成了对浏览器请求的解析,这一篇我们继续来实现响应浏览器的请求,同样的,我们还是先来看一下服务端响应给浏览器的数据格式

HTTP/1.1 200 OKServer: Apache-Coyote/1.1Accept-Ranges: bytesETag: W/"129-1456125361109"Last-Modified: Mon, 22 Feb 2016 07:16:01 GMTContent-Type: text/htmlContent-Length: 129Date: Mon, 22 Feb 2016 08:08:32 GMT<!DOCTYPE html><html><head><meta charset="utf-8"><title>test</title></head><body>this is test page.</body></html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

只要我们响应的数据满足这个格式,浏览器就可以正常解析了,在解析之前还是先来做一些准备工作。 
相应请求时可能会出现异常,所以我们先编写一个异常类,和请求的异常类类似。

package com.gujin.server;/** * 响应异常 *  * @author jianggujin *  */public class HQResponseException extends RuntimeException{   private static final long serialVersionUID = 1L;   public HQResponseException()   {      super();   }   public HQResponseException(String message)   {      super(message);   }   public HQResponseException(String message, Throwable cause)   {      super(message, cause);   }   public HQResponseException(Throwable cause)   {      super(cause);   }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

不管是请求还是响应都需要对字符串进行操作,所以将字符串操作的方法抽取出来形成一个字符串工具类。

package com.gujin.server.utils;/** * 字符串工具 *  * @author jianggujin *  */public class HQString{   /**    * 判断字符串为空    *     * @param s    * @return    */   public static boolean isEmpty(String s)   {      return s == null || s.length() == 0;   }   /**    * 判断字符串是否非空    *     * @param s    * @return    */   public static boolean isNotEmpty(String s)   {      return !isEmpty(s);   }   /**    * 判断字符串是空白字符    *     * @param s    * @return    */   public static boolean isBlack(String s)   {      return isEmpty(s) || s.matches("\\s*");   }   /**    * 判断字符串是非空白字符F    *     * @param s    * @return    */   public static boolean isNotBlack(String s)   {      return !isBlack(s);   }   /**    * 截取字符串    *     * @param s    * @param flag    * @return    */   public static String subStringBefore(String s, String flag)   {      if (isEmpty(s) || isEmpty(flag))      {         return s;      }      int index = s.indexOf(flag);      if (index != -1)      {         return s.substring(0, index);      }      return flag;   }   /**    * 截取字符串    *     * @param s    * @param flag    * @return    */   public static String subStringAfter(String s, String flag)   {      if (isEmpty(s) || isEmpty(flag))      {         return s;      }      int index = s.indexOf(flag);      if (index != -1)      {         return s.substring(index + flag.length());      }      return flag;   }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

好了,准备工作已经基本完成了,下面我们来编写响应类

package com.gujin.server;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.OutputStream;import java.net.Socket;import java.text.MessageFormat;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Locale;import com.gujin.server.utils.HQString;/** * HTTP响应 *  * @author jianggujin *  */public class HQResponse{   /** 缓冲区大小 **/   private final int BUFSIZE = 512;   /** 响应时间格式化 **/   private final String RESPONSE_DATE_TIME = "EEE, dd MMM yyyy HH:mm:ss 'GMT'";   /** HTTP版本 **/   private final String HTTP_VERSION = "HTTP/1.1";   /** 响应时间格式化 **/   private final SimpleDateFormat RESPONSE_DATE_FORMAT = new SimpleDateFormat(         RESPONSE_DATE_TIME, Locale.US);   /** 缓冲输出流 **/   private ByteArrayOutputStream bufferStream = null;   /** Socket输出流 **/   private OutputStream stream = null;   /** 响应码 **/   private int statusCode = 200;   /** 内容类型 **/   private String contentType;   /**    * 构造方法    *     * @param socket    * @throws IOException    */   public HQResponse(Socket socket) throws IOException   {      bufferStream = new ByteArrayOutputStream(BUFSIZE);      stream = socket.getOutputStream();   }   /**    * 向客户端写数据    *     * @param data    * @throws IOException    */   public void write(byte[] data) throws IOException   {      bufferStream.write(data);   }   /**    * 向客户端写数据    *     * @param data    * @param start    * @param len    */   public void write(byte[] data, int start, int len)   {      bufferStream.write(data, start, len);   }   /**    * 向客户端发送头信息    *     * @throws IOException    */   private void writeHeader() throws IOException   {      stream.write(MessageFormat.format("{0} {1} {2}\r\n", HTTP_VERSION,            statusCode, "OK").getBytes());      stream.write(MessageFormat.format("Date: {0}\r\n",            RESPONSE_DATE_FORMAT.format(new Date())).getBytes());      stream.write("Server: HQHttpServer 1.0".getBytes());      if (HQString.isNotEmpty(contentType))      {         stream.write(MessageFormat               .format("Content-Type: {0}\r\n", contentType).getBytes());      }      stream.write(MessageFormat.format("Content-Length: {0}\r\n",            bufferStream.size()).getBytes());   }   /**    * 实际响应    *     * @throws IOException    */   public void response() throws IOException   {      writeHeader();      // 换一行      stream.write("\r\n".getBytes());      bufferStream.writeTo(stream);      bufferStream.flush();      stream.flush();   }   /**    * 获得内容类型    *     * @return    */   public String getContentType()   {      return contentType;   }   /**    * 设置内容类型    *     * @param contentType    */   public void setContentType(String contentType)   {      this.contentType = contentType;   }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131

最后我们对上一篇博客中的HQHttpServer类中的handleRequest方法进行改造,将浏览器请求的头信息响应给浏览器,完成一次交互,完整代码如下:

package com.gujin.server;import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;import java.text.MessageFormat;import java.util.Iterator;import java.util.logging.Level;import com.gujin.server.utils.HQClose;/** * 服务端 *  * @author jianggujin *  */public class HQHttpServer implements HQHttpServerLog{   /** 端口号 **/   private int port = 80;   /** 服务套接字 **/   private ServerSocket serverSocket = null;   /**    * 默认构造方法    */   public HQHttpServer()   {   }   /**    * 构造方法    *     * @param port    */   public HQHttpServer(int port)   {      this.port = port;   }   /**    * 启动服务器    */   public synchronized void start()   {      try      {         serverSocket = new ServerSocket(port);         LOG.info("server init success.");      }      catch (IOException e)      {         LOG.log(Level.SEVERE, e.getMessage(), e);      }      new Thread()      {         public void run()         {            while (!isStop())            {               Socket socket;               try               {                  socket = serverSocket.accept();                  handleRequest(socket);               }               catch (IOException e)               {                  LOG.log(Level.SEVERE, e.getMessage(), e);               }            }         };      }.start();   }   /**    * 处理请求    *     * @param socket    * @throws IOException    */   public void handleRequest(Socket socket) throws IOException   {      HQRequest request = new HQRequest(socket);      request.execute();      HQResponse response = new HQResponse(socket);      response.setContentType("text/plain");      Iterator<String> iterator = request.getHeaderNames();      while (iterator.hasNext())      {         String name = iterator.next();         response.write(MessageFormat.format("{0}: {1}", name,               request.getHeader(name)).getBytes());      }      response.response();      socket.close();   }   /**    * 是否停止    *     * @return    */   public boolean isStop()   {      return serverSocket == null || serverSocket.isClosed();   }   /**    * 停止服务器    */   public synchronized void stop()   {      if (!isStop())      {         HQClose.safeClose(serverSocket);         serverSocket = null;      }   }   public static void main(String[] args)   {      new HQHttpServer().start();   }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127

运行程序,在浏览器输入:http://127.0.0.1,我们可以看到浏览器已经可以正常的接收服务端相应的数据了,页面显示内容如下: 
ACCEPT-ENCODING: gzip,deflate,sdchHOST: 127.0.0.1USER-AGENT: Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36CONNECTION: keep-aliveACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8ACCEPT-LANGUAGE: zh-CN,zh;q=0.8

打开浏览器的开发者工具观察服务端响应的数据信息 
这里写图片描述

我们可以看到浏览器接收到的头信心与我们相应的数据是一致的。 
到这里,一个最简单的WEB服务器已经完成了与浏览器的一次完成整的交互,接下来我们要继续对其优化。

原创粉丝点击