模擬一個簡單的tomcat
簡單處理
// 客戶端和伺服器的通訊,說到底就是兩個數據的傳輸,
// 客戶端發送inputStream給伺服器,伺服器回復
// outputStream給客戶端。
public class TomcatServerV1 {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {//一直監聽,直到受到停止的命令
Socket socket = null;
try {
socket = serverSocket.accept();//如果沒有請求,會一直hold在這裡等待,有客戶端請求的時候才會繼續往下執行
// log
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));//獲取輸入流(請求)
StringBuilder stringBuilder = new StringBuilder();
String line = null;
while ((line = bufferedReader.readLine()) != null && !line.equals("")) {//得到請求的內容,注意這裡作兩個判斷非空和""都要,只判斷null會有問題
stringBuilder.append(line).append("<br>");
}
String result = stringBuilder.toString();
System.out.println(result);
// echo
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);//這裡第二個參數表示自動刷新快取
printWriter.println("HTTP/1.1 200 OK");
printWriter.println("Content-Type:text/html;charset=utf-8");
printWriter.println();
printWriter.println("<h5>你剛才發送的請求數據是:<br>");
printWriter.write(result);//將日誌輸出到瀏覽器
printWriter.println("</h5>");
// release
printWriter.close();
bufferedReader.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
每個請求一個執行緒
class RequestHandler implements Runnable{
private Socket socket;
public RequestHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
// log
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));//獲取輸入流(請求)
StringBuilder stringBuilder = new StringBuilder();
String line = null;
while ((line = bufferedReader.readLine()) != null && !line.equals("")) {
stringBuilder.append(line).append("<br>");
}
String result = stringBuilder.toString();
System.out.println(result);
// echo
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true);//這裡第二個參數表示自動刷新快取
printWriter.println("HTTP/1.1 200 OK");
printWriter.println("Content-Type:text/html;charset=utf-8");
printWriter.println();
printWriter.println("<h5>你剛才發送的請求數據是:<br>");
printWriter.write(result);//將日誌輸出到瀏覽器
printWriter.println("</h5>");
// release
printWriter.close();
bufferedReader.close();
socket.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
public class TomcatServerV2 {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {//一直監聽,直到受到停止的命令
Socket socket = null;
socket = serverSocket.accept();//如果沒有請求,會一直hold在這裡等待,有客戶端請求的時候才會繼續往下執行
RequestHandler requestHandler = new RequestHandler(socket);
new Thread(requestHandler).start();
}
}
}
模擬tomcat
正如前面的部落格:Servlet筆記中記錄:
首先建一個MyServlet介面,然後創建一個抽象類MyHttpServlet繼承介面,最後建一個UserServlet實現具體的doGet,doPost等方法。
pom依賴添加servlet
和dom4j
。DOM4J是dom4j.org出品的一個開源XML
解析包。
項目目錄:
原理圖:
Request.java
package cn.orzlinux.Bean.mytomcat.http;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Request {
// 如/usr
private String uri;
// 請求方法
private String method;
public Request(InputStream inputStream) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader((inputStream)));
//取HTTP請求響應的第一行,GET /user HTTP/1.1,按空格隔開
String[] data = reader.readLine().split(" ");
this.uri = data[1];
this.method = data[0];
} catch (IOException e) {
e.printStackTrace();
}
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
}
response
package cn.orzlinux.Bean.mytomcat.http;
import java.io.OutputStream;
public class Response {
public OutputStream outputStream;
public static final String responsebody="HTTP/1.1 200+\r\n"+"Content-Type:text/html+\r\n"
+"\r\n";
public Response(OutputStream outputStream){
this.outputStream=outputStream;
}
}
MyServlet
package cn.orzlinux.Bean.mytomcat.servlet;
import cn.orzlinux.Bean.mytomcat.http.Request;
import cn.orzlinux.Bean.mytomcat.http.Response;
public interface MyServlet {
void init() throws Exception;
void service(Request request, Response response) throws Exception;
void destroy();
}
MyHttpServlet
package cn.orzlinux.Bean.mytomcat.servlet;
import cn.orzlinux.Bean.mytomcat.http.Request;
import cn.orzlinux.Bean.mytomcat.http.Response;
public abstract class MyHttpServlet implements MyServlet {
public void init() throws Exception {
}
public void service(Request request, Response response) throws Exception {
if("get".equalsIgnoreCase(request.getMethod())) {
this.doGet(request,response);
} else {
this.doPost(request,response);
}
}
public abstract void doGet(Request request,Response response);
public abstract void doPost(Request request,Response response);
public void destroy() {
}
}
userServlet
package cn.orzlinux.Bean.mytomcat.servlet;
import cn.orzlinux.Bean.mytomcat.http.Request;
import cn.orzlinux.Bean.mytomcat.http.Response;
import java.io.IOException;
import java.io.OutputStream;
public class UserServlet extends MyHttpServlet {
public void doGet(Request request, Response response) {
System.out.println("doget");
this.doPost(request,response);
}
public void doPost(Request request, Response response){
OutputStream outputStream = response.outputStream;
String result = Response.responsebody+"user handle successful";
try {
outputStream.write(result.getBytes());
outputStream.flush();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
RequestHandler
package cn.orzlinux.Bean.mytomcat.socket;
import cn.orzlinux.Bean.mytomcat.http.Request;
import cn.orzlinux.Bean.mytomcat.http.Response;
import cn.orzlinux.Bean.mytomcat.servlet.MyHttpServlet;
import java.io.*;
import java.net.Socket;
public class RequestHandler implements Runnable{
private Socket socket;
public RequestHandler(Socket socket) {
this.socket = socket;
}
public void run() {
// log
try {
Request request = new Request(socket.getInputStream());
Response response = new Response(socket.getOutputStream());
String uri = request.getUri();
System.out.println(uri);
String servletName = TomcatServerV3.urlmapping.get(uri);
MyHttpServlet servlet = TomcatServerV3.servletMapping.get(servletName);
if(servlet!=null) {
servlet.service(request,response);
}else{
String resp=Response.responsebody+"can not find servlet";
OutputStream outputStream=socket.getOutputStream();
System.out.println(resp);
outputStream.write(resp.getBytes());
outputStream.flush();
outputStream.close();
}
}catch (Exception e) {
e.printStackTrace();
} finally {
try {
socket.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
TomcatServerV3
package cn.orzlinux.Bean.mytomcat.socket;
import cn.orzlinux.Bean.mytomcat.servlet.MyHttpServlet;
import cn.orzlinux.Bean.mytomcat.socket.RequestHandler;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
public class TomcatServerV3 {
public static final int port = 8080;
// 定義mytomcat.xml中的兩個映射
public static final HashMap<string, myhttpservlet=""> servletMapping = new HashMap<string, myhttpservlet="">();
public static final HashMap<string,string> urlmapping=new HashMap<string, string="">();
public static void main(String[] args) throws IOException {
TomcatServerV3 tomcatServerV3 = new TomcatServerV3();
tomcatServerV3.init();
tomcatServerV3.run();
}
// 初始化,載入xml裡面的servlet資訊
private void init() {
try {
String path = TomcatServerV3.class.getResource("/").getPath();
SAXReader reader = new SAXReader();
Document document = reader.read(new File(path+"mytomcat.xml"));
Element rootelement = document.getRootElement();
List<element> elements = rootelement.elements();
for(Element element:elements) {
if ("servlet".equalsIgnoreCase(element.getName())){
Element servletname=element.element("servlet-name");
Element servletclass=element.element("servlet-class");
System.out.println(servletname.getText()+"==>"+servletclass.getText());
//需要注意的是servletMapping映射的第二個參數,要通過反射的方式進行實例化
servletMapping.put(servletname.getText(),
(MyHttpServlet) Class.forName(servletclass.getText().trim()).newInstance());
}else if ("servlet-mapping".equalsIgnoreCase(element.getName())){
Element servletname=element.element("servlet-name");
Element urlpattern=element.element("url-pattern");
System.out.println(servletname.getText()+"==>"+urlpattern.getText());
urlmapping.put(urlpattern.getText(),servletname.getText());
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
private void run() {
ServerSocket serverSocket= null;
try {
serverSocket = new ServerSocket(port);
System.out.println("====服務啟動====");
while(!serverSocket.isClosed()){
Socket socket=serverSocket.accept();
RequestHandler requestHandler=new RequestHandler(socket);
new Thread(requestHandler).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
輸出:
userServlet==>cn.orzlinux.Bean.mytomcat.servlet.UserServlet
userServlet==>/user
====服務啟動====
/
HTTP/1.1 200+
Content-Type:text/html+
can not find servlet
/user
doget
最後再回顧一下原理圖:
參考
從零開始手寫Tomcat,一文徹底搞懂Tomcat運行流程(附源碼)
本文同步發佈於orzlinux.cn