JAVA简易WEB服务器(三)

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

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"129-1456125361109"
Last-Modified: Mon, 22 Feb 2016 07:16:01 GMT
Content-Type: text/html
Content-Length: 129
Date: 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>

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

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);
   }
}

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

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;
   }
}

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

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;
   }
}

最后我们对上一篇博客中的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();
   }
}

运行程序,在浏览器输入: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服务器已经完成了与浏览器的一次完成整的交互,接下来我们要继续对其优化。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值