目的
- 理解服務器的核心容器以及servlet工作原理。
服務器實現步驟
-
瀏覽器發起的每個請求,都需要解析和響應,對請求報文解析,和響應報文部分內容封裝請求對象和響應對象(HttpServletRequest和HttpServletResponse都是接口,由org.apache.catalina.connector(Tomcat)包下面的RequestFacade和ResponseFacade實現並進行實例化)。
-
定義處理服務類的標準格式(MyServlet)
-
加載核心容器(MyServlet容器),開啓監聽
-
服務器端多線程處理
-
ps:socket是對TCP/IP協議的封裝。
req和resp的定義
- 遵循http協議,解析請求、響應報文。
- MyReq.java
public class MyReq {
private String method;
private String url;
public MyReq(InputStream is) {
BufferedReader br = new BufferedReader(new InputStreamReader(is));
try {
String reqHeader = br.readLine();
String[] reqHeaderArr = reqHeader.split(" ");
this.method = reqHeaderArr[0];
this.url = reqHeaderArr[1];
} catch (IOException e) {
e.printStackTrace();
}
}
public String getMethod() {
return method;
}
public String getUrl() {
return url;
}
}
- MyResp.java
public class MyResp {
public static String respHeader = "HTTP/1.1 200 OK\r\n" +
"Content-Type:text/html;charset=utf-8\r\n" +
"\r\n";
private OutputStream writer;
public MyResp(OutputStream os) {
this.writer = os;
}
public OutputStream getWriter() {
return writer;
}
}
MyServlet模板的定義
- MyServlet.java
public abstract class MyServlet {
public void doService(MyReq req, MyResp resp) {
String reqMethod = req.getMethod();
if ("GET".equals(reqMethod)) {
doGet(req, resp);
} else {
doPost(req, resp);
}
}
public abstract void doGet(MyReq req, MyResp resp);
public abstract void doPost(MyReq req, MyResp resp);
}
- 編寫兩個類繼承MyServlet模板,以作測試
public class LoginServlet extends MyServlet {
@Override
public void doGet(MyReq req, MyResp resp) {
OutputStream respWriter = resp.getWriter();
try {
respWriter.write(MyResp.respHeader.getBytes());
respWriter.write("login ok!".getBytes());
respWriter.flush();
respWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doPost(MyReq req, MyResp resp) {
doGet(req, resp);
}
}
public class RegistServlet extends MyServlet {
@Override
public void doGet(MyReq req, MyResp resp) {
OutputStream respWriter = resp.getWriter();
try {
respWriter.write(MyResp.respHeader.getBytes());
respWriter.write("register ok!".getBytes());
respWriter.flush();
respWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void doPost(MyReq req, MyResp resp) {
doGet(req, resp);
}
}
加載核心容器
- 配置文件:myMapping.properties
servlet.one.url=/login
servlet.one.class=com.myserver.servlet.LoginServlet
servlet.two.url=/regist
servlet.two.class=com.myserver.servlet.RegistServlet
- MyServer.java
public class MyServer {
static Map<String, MyServlet> myMapping = new HashMap<>();// 核心容器
static Properties prop = new Properties();
public static void myInit() {
try {
// 讀取配置文件
prop.load(MyServer.class.getResourceAsStream("/myMapping.properties"));
Set<Object> set = prop.keySet();
set.forEach(k -> {
if (k.toString().contains("url")) {
// 獲得url的servlet的對應
String url = prop.getProperty(k.toString());
String classKey = k.toString().replace("url", "class");
String className = prop.getProperty(classKey);
try {
// 通過類名獲得實例
MyServlet myms = (MyServlet) Class.forName(className).newInstance();
// 將url與myServlet的映射關係存入映射表中
myMapping.put(url, myms);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
public static void serverBegin() {
try {
ServerSocket ss = new ServerSocket(8021);
System.out.println("開始監聽 8021 端口,等待訪問...");
while (true) {
// 同時滿足多人訪問,多線程
Socket accept = ss.accept();
Thread myThread = new Thread(new MyProccess(accept));
myThread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 啓動服務器
* @param args
*/
public static void main(String[] args) {
MyServer.myInit();
MyServer.serverBegin();
}
}
服務器端多線程訪問處理
- MyProccess.java
public class MyProccess implements Runnable {
private Socket accept;
public MyProccess(Socket accept) {
this.accept = accept;
}
@Override
public void run() {
try {
// 服務器針對瀏覽器的每一次請求創建req和resp對象
MyReq req = new MyReq(accept.getInputStream());
MyResp resp = new MyResp(accept.getOutputStream());
// myMapping即爲核心容器
MyServlet myServlet = MyServer.myMapping.get(req.getUrl());
if (myServlet != null) {
myServlet.doService(req, resp);
} else {
OutputStream respWriter = resp.getWriter();
respWriter.write(MyResp.respHeader.getBytes());
respWriter.write("404 not found!".getBytes());
respWriter.flush();
respWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}