How tomcat work连载一:简易的静态WEB容器
以下代码是我在学习《HOW Tomcat work》第一章:如何构建一个简单的静态文件容器后,写下的,注释很详细,不懂的可以站内短信我.
首先建立一个监听Server类,如下所示:
package ex01.pyrmont;
import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.Date; import org.apache.log4j.Logger; /** * 类HttpServer.java的实现描述: */ public class HttpServer { public static final Logger log = Logger.getLogger("actionLog"); /** 获取当前的文件系统路径 **/ public static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webRoot"; /** 关闭的命令 **/ public static final String SHUT_DOWN = "/shutdown"; private ServerSocket server; /** * 构造监听server * * @param port 监听的端口 * @param backlog 监听队列的最大长度 * @param serverName 主机名(可以是域名或者是IP) */ public HttpServer(int port, int backlog, String serverName){ try { server = new ServerSocket(port, backlog, InetAddress.getByName(serverName)); } catch (IOException e) { log.error(e.getMessage(), e); } } public static void main(String[] args) { HttpServer server = new HttpServer(8773, 1, "127.0.0.1"); try { server.listen(); /** 当监听终止的时候,释放相关资源 **/ server.close(); } catch (IOException e) { log.error(e.getMessage(), e); } } /** * 启动server的监听程序<br/> * 当在等待连接的时候,该方法可能会抛出<code>IOException</code><br/> * 例如server意外关闭等等 * * @throws IOException */ public void listen() throws IOException { if (log.isInfoEnabled()) { log.info("server begin to listen on :" + new Date()); } boolean isShutdown = false; Socket client = null; InputStream is = null; OutputStream os = null; Request request; Response response; while (!isShutdown) { client = server.accept(); if (log.isInfoEnabled()) { log.info("get Client:" + client); } is = client.getInputStream(); os = client.getOutputStream(); /** 构造请求 **/ request = new Request(is); /** 提取请求中的参数 **/ request.parse(); response = new Response(request); response.setOutput(os); /** 发送静态资源 **/ response.sendStaticMessage(); /** 关闭当前客户端 **/ client.close(); /** 判断是否是关闭命令 **/ isShutdown = request.getUri().equalsIgnoreCase(SHUT_DOWN); } } /** * 关闭server */ public void close() { if (server != null && !server.isClosed()) { try { if (log.isInfoEnabled()) { log.info("server end to listen on :" + new Date()); } server.close(); } catch (IOException e) { log.error(e.getMessage(), e); } } } }
建立一个对应静态文件的请求类:
package ex01.pyrmont;
import java.io.IOException; import java.io.InputStream; import org.apache.log4j.Logger; /** * 类Request.java的实现描述: */ public class Request { private static final Logger log = Logger.getLogger("actionLog"); private InputStream is; private String uri; public Request(InputStream is){ this.is = is; } /** * 读取request的内容,并提炼出uri */ public void parse() { StringBuffer sb = new StringBuffer(2048); byte[] buffers = new byte[2048]; int i; try { i = is.read(buffers); } catch (IOException e) { log.error(e.getMessage(), e); i = -1; } for (int j = 0; j < i; j++) { sb.append((char) buffers[j]); } if (log.isInfoEnabled()) { log.info("request:" + sb.toString()); } this.uri = parseUri(sb.toString()); } private String parseUri(String request) { /** 解析HTTP请求的头部,像GET/POST /index.html HTTP1.1 **/ int index1, index2; /** 获取第一个空格的位置 **/ index1 = request.indexOf(' '); if (index1 > -1) { /** 从第一个空格之后,获取第二个空格的位置 **/ index2 = request.indexOf(' ', index1 + 1); if (index2 > index1) { return request.substring(index1 + 1, index2); } } return null; } /** * 返回request的uri路径 * * @return uri */ public String getUri() { return uri; } @Override public String toString() { return String.format("Request[uri=%s]", this.uri); } }
建立响应输出类:
package ex01.pyrmont;
import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.log4j.Logger; /** * 类Response.java的实现描述: */ public class Response { private static final Logger log = Logger.getLogger("actionLog"); /** 缓冲区的大小 **/ private static final int BUFFER_SIZE = 1024; private Request request; private OutputStream output; public Response(Request request){ this.request = request; } /** * 初始化一个输出的缓冲流 * * @param os */ public void setOutput(OutputStream os) { output = os; } /** * 发送静态消息 */ public void sendStaticMessage() { InputStream is = null; String uri = request.getUri(); /** 如果uri为null或者等同与关闭命令时候,直接返回 **/ if (uri == null || uri.equalsIgnoreCase(HttpServer.SHUT_DOWN)) { return; } try { File file = new File(HttpServer.WEB_ROOT, uri); /** 如果静态资源文件找不到,则输出一个错误提示页面 **/ if (!file.exists()) { log.warn("can't find uri:" + uri); String outputHtml = "<html>" + "<body>" + "<div><h1>can't find your response:" + uri + "</h1></div>" + "</body>" + "</html>"; output.write(outputHtml.getBytes()); } else { is = new FileInputStream(file); byte[] buffers = new byte[BUFFER_SIZE]; /** 读入缓冲区的总字节数 **/ int ch = is.read(buffers, 0, BUFFER_SIZE); while (ch != -1) { output.write(buffers, 0, ch); ch = is.read(buffers, 0, BUFFER_SIZE); } } } catch (IOException e) { log.error(e.getMessage(), e); } closeResponse(is); } /** * 逆序的关闭响应的输出流,并输出响应文件 * * @param is * @param isr * @param br */ private void closeResponse(InputStream is) { if (is != null) { try { is.close(); } catch (IOException e) { log.error(e.getMessage(), e); } } } }
最后我们测试下:
建立一个简单的HTML文件,如下所示:
<html>
<body> <h1>welcome to Sweet's home</h1> </body> </html>
试着在浏览器里输入http://127.0.0.1:8773/index.html,是不是可以看到这个欢迎页面
如果输入http://127.0.0.1:8773/other.html(一个不存在的页面),是不是可以看到我们的错误页面提示
如果输入http://127.0.0.1:8773/shutdown,可以看到我们的简易服务器正常关闭。