目的
- 理解服务器的核心容器以及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();
}
}
}