野生Web容器之TomJetty之掀起你的盖头来
上文我们对于实现这个野生Web容器做了一些知识铺垫和复习,息知了HTTP请求的头部的组成元素,本文我们主要讲解如何在代码中去解析它的各个部分,掀起盖头来,给哥笑一个^_^。在这之前,我们需要建立服务器,把框架搭建起来。
一、服务器建立
1.新建一个名为TomJetty的Java工程。
2.在工程src目录下新建一个tomjetty.config文件,用于提供服务器配置参数。
- tomjetty.port=8080
- tomjetty.requestheader.class=cn.lynn.tomjetty.RequestHeaderParserImpl
3.编写一个工具类TomJettyUtil,用于程序读取配置参数值。
- package cn.lynn.tomjetty;
- import java.io.IOException;
- import java.util.Properties;
- public class TomJettyUtil {
- private static Properties props = new Properties();
- static {
- try {
- props.load(TomJettyUtil.class.getClassLoader().getResourceAsStream(“tomjetty.config”));
- } catch (IOException e) {
- e.printStackTrace();
- System.exit(0);
- }
- }
- public static String getValue(String key) {
- return props.getProperty(key);
- }
- }
4.编写一个TomJetty类继承于Thread类,用于封装服务器对象
- public class TomJetty extends Thread {
- private static ServerSocket server;
- private Socket socket;
- public TomJetty(Socket socket) {
- this.socket = socket;
- }
- …
- }
5.为TomJetty类提供openServer()和closeServer()方法,用户打开和关闭服务器。
- public static void openServer() throws Exception {
- server = new ServerSocket(Integer.parseInt(TomJettyUtil
- .getValue(“tomjetty.port”)));
- while (true) {
- new TomJetty(server.accept()).start();
- }
- }
- public static void closeServer() throws Exception {
- if (server != null) {
- if (!server.isClosed()) {
- server.close();
- }
- }
- }
6.编写一个StartTomJetty类,用于开启服务器。
- package cn.lynn.tomjetty;
- public class StartTomJetty {
- public static void main(String[] args) {
- try {
- TomJetty.openServer();
- } catch (Exception e) {
- try {
- TomJetty.closeServer();
- } catch (Exception e1) {
- e1.printStackTrace();
- }
- }
- }
- }
这样以来,服务器就能运行了。当然,它什么也不会做。因为我们在run()方法中没有进行任何操作^_^。不要急,休息一下,后面就会讲到,另外代码部分也会随着章节的深入而重构。
二、HTTP请求头解析
1.编写一个RequestHeader类,用户封装请求头对象。
- package cn.lynn.tomjetty;
- import java.util.HashMap;
- /**
- * 封装请求头
- * @author lynnli1229
- */
- public class RequestHeader {
- private String method;
- private String url;
- private String protocal;
- private String accept;
- private String accept_language;
- private String user_agent;
- private String accept_encoding;
- private String ip;
- private String port;
- private String connection;
- private String cookie;
- // 存放请求头键值对
- private HashMap<String, String> map;
- // 存放请求头文本
- private String txt;
- // 省略getter()和setter()方法
- @Override
- public String toString() {
- return “RequestHeader [” + “\n”
- + method + ” ” + url + ” ” + protocal + “\n”
- + “Accept: ” + accept + “\n”
- + “Accept-Language: ” + accept_language + “\n”
- + “User-Agent: ” + user_agent + “\n”
- + “Accept-Encoding: ” + accept_encoding + “\n”
- + “Host: ” + ip + “:” + port + “\n”
- + “Connection: ” + connection + “\n”
- + “Cookie: ” + cookie + “\n”
- + “]”;
- }
- }
2.设计一个IRequestHeaderParser接口,并声明parse()方法,用于解析请求头文本内容。
- package cn.lynn.tomjetty;
- public interface IReqestHeaderParser {
- public RequestHeader parse(String txt) throws Exception;
- }
3.提供IRequestHeaderParser接口的实现类RequestHeaderParserImpl,用于执行解析操作。注意一下,我们是以IE浏览器请求头为例,其他版本浏览器请求头的参数顺序会有变动,但整体差别不大。
- package cn.lynn.tomjetty;
- import java.util.HashMap;
- public class RequestHeaderParserImpl implements IReqestHeaderParser {
- /**
- * 解析HTTP请求头
- */
- public RequestHeader parse(String txt) throws Exception {
- RequestHeader header = new RequestHeader();
- header.setTxt(txt);
- // 截取请求头第一行
- String firstLine = txt.substring(0, txt.indexOf(“\n”));
- String method = firstLine.substring(0, firstLine.indexOf(” “));
- String url = firstLine.substring(firstLine.indexOf(” “) + 1, firstLine.lastIndexOf(” “));
- String protocal = firstLine.substring(firstLine.lastIndexOf(” “) + 1, firstLine.length());
- header.setMethod(method);// 获取Accept参数值,存放到RequestHeader对象当中
- header.setUrl(url);// 获取Accept参数值,存放到RequestHeader对象当中
- header.setProtocal(protocal);// 获取Accept参数值,存放到RequestHeader对象当中
- String[] lines = txt.split(“\n”);
- HashMap<String, String> map = new HashMap<String, String>();
- // 从请求头第二行开始分隔,因为第一行没有冒号
- for (int i = 1; i < lines.length; i++) {
- map.put(lines[i].split(“: “)[0], lines[i].split(“: “)[1]);
- }
- header.setMap(map);
- header.setAccept(map.get(“Accept”)); // 获取Accept参数值,存放到RequestHeader对象当中
- header.setAccept_language(map.get(“Accept-Language”)); // 获取Accept-Language参数值,存放到RequestHeader对象当中
- header.setUser_agent(map.get(“User-Agent”)); // 获取User-Agent参数值,存放到RequestHeader对象当中
- header.setAccept_encoding(map.get(“Accept-Encoding”)); // 获取Accept-Encoding参数值,存放到RequestHeader对象当中
- header.setIp(map.get(“Host”).split(“:”)[0]); // 获取Host参数的IP参数值,存放到RequestHeader对象当中
- header.setPort(map.get(“Host”).split(“:”)[1]); // 获取Host参数的Port参数值,存放到RequestHeader对象当中
- header.setConnection(map.get(“Connection”)); // 获取Connection参数值,存放到RequestHeader对象当中
- header.setCookie(map.get(“Cookie”)); // 获取Cookie参数值,存放到RequestHeader对象当中
- return header;
- }
- }
4.在TomJetty类的run()方法中加入如下代码,用于将解析请求头的结果打印到控制台上。
- try {
- InputStream in = socket.getInputStream(); // 获取客户端发送的字节流
- OutputStream out = socket.getOutputStream(); // 获取服务端响应的字节流
- byte[] b = new byte[1024 * 1024]; // 设置字节缓冲区
- in.read(b); // 读取客户端字节流(字节流的请求头)
- String txt = new String(b).trim(); // 将请求头封装成String,准备交给解析器解析
- IReqestHeaderParser parser = (IReqestHeaderParser) Class.forName(TomJettyUtil.getValue(“tomjetty.requestheader.class”)).newInstance();
- RequestHeader header = parser.parse(txt); // 终于可以解析了
- System.out.println(header);
- } catch (Exception e) {
- e.printStackTrace();
- }
三、运行效果展示
在IE浏览器中输入上述地址后,控制台打印如下:
- RequestHeader [
- GET /hello.jsp HTTP/1.1
- Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/QVOD, application/QVOD, */*
- Accept-Language: zh-cn
- User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; KB974488)
- Accept-Encoding: gzip, deflate
- Host: 127.0.0.1:8080
- Connection: Keep-Alive
- Cookie: null
- ]
写到这里,我们终于完成了对HTTP请求头的解析操作,代码比预想的要多,其中字符串的拆分是关键,其他也没什么了。