netty之Tomcat
创建一个Tomcat,使用原生api
手写一个简单的Tomcat服务器,需要的东西有:request和response,分别处理请求和响应,当用户发送http请求时,request负责接收发送过来的请求体,然后进行解析,调用对应的servlet进行业务处理,然后通过response响应给客户端。
- 首先需要定义一个servlet负责处理get请求或post 请求。
- 用户通过创建servlet继承我们提供的servlet来自定义自己的方法,执行自己的业务逻辑
- 还需要创建request类,负责接收请求,并对接收到的数据进行解析,找到要执行的方法
- response负责处理响应给客户端的数据
- 这里通过web.properties的方式进行配置,因此还需要读取配置文件进行解析获取到方法的映射也就是将方法名和路径关联起来
- 如此就可以实现一个简单的Tomcat服务器。
开始
创建servlet抽象类
package com.lany.mytomcat;
/**
* @author liuyanyan
* @date 2021/12/14 15:21
*/
public abstract class GPServlet {
public void service(GPRequest request,GPResponse response) throws Exception {
if("GET".equalsIgnoreCase(request.getMethod())){
doGet(request,response);
}else{
doPost(request,response);
}
}
protected abstract void doPost(GPRequest request, GPResponse response) throws Exception;
protected abstract void doGet(GPRequest request, GPResponse response) throws Exception;
}
用户自定义自己的servlet
package com.lany.mytomcat;
/**
* @author liuyanyan
* @date 2021/12/14 15:24
*/
public class FirstServlet extends GPServlet {
@Override
protected void doPost(GPRequest request, GPResponse response) throws Exception {
response.write("This is First Servlet");
}
@Override
protected void doGet(GPRequest request, GPResponse response) throws Exception {
this.doPost(request, response);
}
}
配置web.properties
servlet.one.url=/firstServlet.do
servlet.one.className=com.lany.mytomcat.FirstServlet
servlet.two.url=/secondServlet.do
servlet.two.className=com.lany.mytomcat.SecondServlet
创建GPRequest类
request对象就是通过读取Http请求头的信息进行解析,后台服务器获得到的数据就是一段字符串
GET /firstServlet.do HTTP/1.1
Host: localhost:8080
Connection: keep-alive
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Microsoft Edge";v="96"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36 Edg/96.0.1054.53
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
在request中通过解析获取到请求的方法和请求的路径
package com.lany.mytomcat;
import java.io.InputStream;
/**
* @author liuyanyan
* @date 2021/12/14 15:22
*/
public class GPRequest {
private String method;
private String url;
public GPRequest(InputStream in) {
try {
// 获取http内容
String content = "";
byte[] buff = new byte[1024];
int len = 0;
if ((len = in.read(buff)) > 0) {
content = new String(buff, 0, len);
}
System.out.println(content);
String line = content.split("\\n")[0];
String[] arr = line.split("\\s");
this.method = arr[0];
this.url = arr[1].split("\\?")[0];
} catch (Exception e) {
e.printStackTrace();
}
}
public String getUrl() {
return url;
}
public String getMethod() {
return method;
}
}
创建GPResponse 类
response按照HTTP规范输出格式化的字符串
package com.lany.mytomcat;
import java.io.OutputStream;
/**
* @author liuyanyan
* @date 2021/12/14 15:22
*/
public class GPResponse {
private OutputStream out;
public GPResponse(OutputStream out) {
this.out = out;
}
public void write(String s) throws Exception {
StringBuilder sb = new StringBuilder();
sb.append("Http/1.1 200 OK\n")
.append("Content-Type: text/html;\n")
.append("\r\n")
.append(s);
out.write(sb.toString().getBytes());
}
}
创建GPTomcat类
package com.lany.mytomcat;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* @author liuyanyan
* @date 2021/12/14 15:53
*/
public class GPTomcat {
private int port = 8080;
private ServerSocket server;
private Map<String, GPServlet> servletMapping = new HashMap<>();
private Properties webxml = new Properties();
public static void main(String[] args) {
new GPTomcat().start();
}
private void init() {
try {
String WEB_INF = this.getClass().getResource("/").getPath();
FileInputStream fis = new FileInputStream(WEB_INF + "web.properties");
webxml.load(fis);
for (Object k : webxml.keySet()) {
String key = k.toString();
// System.out.println("key:" + key);
if (key.endsWith(".url")) {
String servletName = key.replaceAll("\\.url$", "");
// System.out.println("servletName: " + servletName);
String url = webxml.getProperty(key);
System.out.println("url: " + url);
String className = webxml.getProperty(servletName + ".className");
System.out.println("className:" + className);
// 单实例,多线程
GPServlet obj = (GPServlet) Class.forName(className).newInstance();
servletMapping.put(url, obj);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void start() {
init();
try {
server = new ServerSocket(this.port);
System.out.println("GPTomcat 已启动,监听端口是:" + this.port);
while (true) {
Socket client = server.accept();
process(client);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void process(Socket client) throws Exception {
InputStream is = client.getInputStream();
OutputStream os = client.getOutputStream();
GPRequest request = new GPRequest(is);
GPResponse response = new GPResponse(os);
String url = request.getUrl();
if (servletMapping.containsKey(url)) {
servletMapping.get(url).service(request, response);
} else {
response.write("404 - Not Found");
}
os.flush();
os.close();
is.close();
client.close();
}
}