一、前言
当我们的Web运行的时候,从浏览器发出的请求,必然首先到达tomcat中,之后由tomcat进行处理,由此要考虑tomcat要进行哪些处理,首先便是提供Socket服务,之后对于请求进行分发,把请求和产生的响应封装成request和response
(1)提供Socket服务
(2)封装请求/响应对象
(3)将不同的请求映射到具体的Servlet处理
这里我们采用nio实现。
二、代码实现
1)封装请求对象:通过对HTTP协议进行解析,拿到了HTTP请求头的方法和URL。
public class MyRequest {
private String url;
private String method;
public MyRequest(String httpRequest) {
String httHed = httpRequest.split("\n")[0];
url = httHed.split("\\s")[1];
method = httHed.split("\\s")[0];
System.out.println(this);
}
public String getUrl() {
return url;
}
public String getMethod() {
return method;
}
public void setUrl(String url) {
this.url = url;
}
public void setMethod(String method) {
this.method = method;
}
}
2)封装响应对象:基于HTTP协议的格式进行输出写入。
public class MyResponse {
private SocketChannel channel;
public MyResponse(SocketChannel channel) {
this.channel = channel;
}
public void write(String content) {
try {
StringBuffer httpResponse = new StringBuffer();
httpResponse.append("HTTP/1.1 200 OK\n").append("Content-Type: text/html\n")
.append("\r\n").append("<html><body>").append(content).append("</body></html>");
ByteBuffer outbuffer = ByteBuffer.wrap(httpResponse.toString().getBytes());
channel.write(outbuffer);
channel.close();
} catch (Exception e) {
} finally {
}
}
}
3)servlet请求处理基类:Tomcat是满足Servlet规范的容器,所以Tomcat需要提供API:doGet/doPost/service。
public abstract class MyServlet {
public void service(MyRequest myRequest, MyResponse myResponse) {
if (myRequest.getMethod().equalsIgnoreCase("POST")) {
doPost(myRequest, myResponse);
} else if (myRequest.getMethod().equalsIgnoreCase("GET")) {
doGet(myRequest, myResponse);
}
}
public void doGet(MyRequest myRequest, MyResponse myResponse) {
}
public void doPost(MyRequest myRequest, MyResponse myResponse) {
}
}
4)Servlet实现类:提供1个实现类,用于测试。
public class FirstServlet extends MyServlet {
@Override
public void doGet(MyRequest myRequest, MyResponse myResponse) {
super.doGet(myRequest, myResponse);
}
@Override
public void doPost(MyRequest myRequest, MyResponse myResponse) {
super.doPost(myRequest, myResponse);
}
@Override
public void service(MyRequest myRequest, MyResponse myResponse) {
myResponse.write("测试");
}
}
5)Servlet配置:对比之前在web开发中,会在web.xml中通过和指定哪个URL交给哪个servlet来处理。
public class ServletMappingConfig {
public static List<ServletMapping> servletMappingList = new ArrayList<>();
static {
servletMappingList.add(new ServletMapping("get", "/get", "com.spring.tomcat.FirstServlet"));
}
}
6)启动类:
tomcat的处理流程:把URL对应处理的Servlet关系形成,解析HTTP协议,封装请求/响应对象,利用反射实例化具体的Servlet进行处理。
public class MyTomcat {
private Selector selector;
private Integer port = 8080; // 定义8080端口
private Map<String, String> urlServletMapping = new HashMap<>(); // 存储url和对应的类
public MyTomcat(Integer port) throws IOException {
this.port = port;
init();
for (ServletMapping servletMapping : ServletMappingConfig.servletMappingList) {
urlServletMapping.put(servletMapping.getUrl(), servletMapping.getClazz());
}
}
public void init() throws IOException {
// 创建一个选择器
selector = Selector.open();
// 创建一个ServerSocketChanel
ServerSocketChannel channel = ServerSocketChannel.open();
// 设置非阻塞
channel.configureBlocking(false);
// 打开一个ServerSocket
ServerSocket serverSocket = channel.socket();
// 地址
InetSocketAddress address = new InetSocketAddress(this.port);
// 绑定
serverSocket.bind(address);
// 注册事件
channel.register(this.selector, SelectionKey.OP_ACCEPT);
}
public void start() throws IOException {
while (true) {
this.selector.select();
Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
// 这个请求是客户端的连接请求事件
accept(key);
} else if (key.isReadable()) {
// 如果这个请求是读事件
read(key);
}
}
}
}
private void accept(SelectionKey key) throws IOException {
// 事件中传过来的,key我们把这个通道拿到
ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
SocketChannel channel = serverSocketChannel.accept();
// 把这个设置为非阻塞
channel.configureBlocking(false);
// 注册读事件
channel.register(selector, SelectionKey.OP_READ);
}
private void read(SelectionKey key) throws IOException {
// 创建一个缓冲区
ByteBuffer buffer = ByteBuffer.allocate(1024);
SocketChannel channel = (SocketChannel)key.channel();
// 我们把通道的数据填入缓冲区
channel.read(buffer);
String request = new String(buffer.array()).trim();
System.out.println("客户端的请求内容" + request);
// 把我们的html内容返回给客户端
MyRequest myRequest = new MyRequest(request);
MyResponse myResponse = new MyResponse(channel);
dispatch(myRequest, myResponse);
}
// 分发请求
@SuppressWarnings("unchecked")
public void dispatch(MyRequest myRequest, MyResponse myResponse) {
String clazz = urlServletMapping.get(myRequest.getUrl());
try {
Class<MyServlet> myServletClass = (Class<MyServlet>)Class.forName(clazz);
if (myServletClass != null) {
MyServlet myservlet = myServletClass.newInstance();
myservlet.service(myRequest, myResponse);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
MyTomcat server = new MyTomcat(8080);
server.start();
}
}
评论区