tomcat学习笔记手写tomcat
Posted 拐柒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了tomcat学习笔记手写tomcat相关的知识,希望对你有一定的参考价值。
tomcat学习笔记(二)手写tomcat
手写实现迷你版tomcat
名称:Minicat
Minicat:作为服务器软件提供服务,我们通过浏览器客户端发送HTTP请求,Minicat可以接受请求进行处理,处理之后的结果可以返回浏览器客户端。
1、提供服务,接受请求(socket通信)
2、请求信息封装成request/response对象
3、客户端请求资源,资源分为静态资源(html)和动态资源(servlet)
4、资源返回给客户端浏览器
Bootstrap
迷你tomcta启动类。
public class Bootstrap
/**
* 定义socket监听端口号
*/
private int port=8080;
public int getPort()
return port;
public void setPort(int port)
this.port = port;
/**
*Minicat启动需要初始化展开的一些操作
*/
public void start() throws Exception
//加载解析配置文件web.xml
loadServlet();
//定义线程池
//初始大小
int corePoolSize=10;
//最大大小
int maximumPoolSize=50;
//存活时长
long keepAliveTime=100L;
//时长单位
TimeUnit unit= TimeUnit.SECONDS;
//请求队列
BlockingQueue<Runnable> workQueue=new ArrayBlockingQueue<>(50);
//线程工厂
ThreadFactory threadFactory=Executors.defaultThreadFactory();
//拒绝策略
RejectedExecutionHandler handler=new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);
/**
* 1.0
* 完成minicat1.0版本(浏览器请求localhost:8080,返回固定的字符串到页面"hellow Minicat!")
*/
ServerSocket serverSocket=new ServerSocket(port);
System.out.println("Minicat start on port:"+port);
// while (true)
// Socket socket = serverSocket.accept();
// //有了socket以后,接收到请求,获取输出流
// OutputStream outputStream = socket.getOutputStream();
// String data="Hello Minicat!";
// String responseText=HttpProtocolUtil.getHttpHeader200(data.getBytes().length)+data;
// outputStream.write(responseText.getBytes());
// socket.close();
//
/**
* 2.0
* 完成minicat2.0版本
* 封装Request和Response对象,返回html静态资源文件
*/
// while (true)
// Socket socket = serverSocket.accept();
// InputStream inputStream = socket.getInputStream();
// //封装request/response对象
// Request request=new Request(inputStream);
// Response response=new Response(socket.getOutputStream());
// response.outPutHtml(request.getUrl());
// socket.close();
//
/**
* 3.0
* 请求动态资源servlet
*/
// while (true)
// Socket socket = serverSocket.accept();
// InputStream inputStream = socket.getInputStream();
// //封装request/response对象
// Request request=new Request(inputStream);
// Response response=new Response(socket.getOutputStream());
// if(servletMap.get(request.getUrl())==null)
// response.outPutHtml(request.getUrl());
// else
// HttpServlet httpServlet = servletMap.get(request.getUrl());
// httpServlet.service(request,response);
//
// socket.close();
//
/**
* 不适用线程池
* 多线程改造
*/
// while (true)
// Socket socket = serverSocket.accept();
// RequestProcessor requestProcessor=new RequestProcessor(socket,servletMap);
// requestProcessor.start();
//
System.out.println("使用线程池进行多线程改造");
/**
* 使用线程池
* 多线程改造
*/
while (true)
Socket socket = serverSocket.accept();
RequestProcessor requestProcessor=new RequestProcessor(socket,servletMap);
threadPoolExecutor.execute(requestProcessor);
private Map<String,HttpServlet> servletMap=new HashMap<>();
//加载解析web.xml初始化servlet
private void loadServlet()
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
SAXReader saxReader=new SAXReader();
try
Document document = saxReader.read(resourceAsStream);
Element rootElement = document.getRootElement();
List<Element> list = rootElement.selectNodes("//servlet");
for (int i = 0; i < list.size(); i++)
Element element = list.get(i);
Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
//servlet-name>lagouServlet</servlet-name>
String servletName=servletNameElement.getStringValue();
Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
//<servlet-class>server.LagouServlet</servlet-class>
String servletClass=servletClassElement.getStringValue();
//根据servletName的值找到url-pattern
Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
//<url-pattern>/lagou</url-pattern>
String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
try
servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());
catch (InstantiationException e)
e.printStackTrace();
catch (IllegalAccessException e)
e.printStackTrace();
catch (ClassNotFoundException e)
e.printStackTrace();
catch (DocumentException e)
e.printStackTrace();
/**
* Minicat启动入口
* @param args
*/
public static void main(String[] args)
Bootstrap bootstrap=new Bootstrap();
try
//启动Minicat
bootstrap.start();
catch (Exception e)
e.printStackTrace();
HttpProtocolUtil
http协议工具类,
/**
* http协议工具类,主要是提供响应头信息,这里我们提供200和404的情况
*/
public class HttpProtocolUtil
/**
* 为响应码200提供请求头信息
* @return
*/
public static String getHttpHeader200(long contentLength)
return "HTTP/1.1 200 OK \\n" +
"Content-Type: text/html;charset=utf-8 \\n" +
"Content-Length: " +contentLength+" \\n" +
"\\r\\n";
/**
* 为响应码404提供请求头信息(此处也包含了数据内容)
* @return
*/
public static String getHttpHeader404()
String str="<h1>404 not found</h1>";
return "HTTP/1.1 404 NOT Found \\n" +
"Content-Type: text/html;charset=utf-8 \\n" +
"Content-Length: " +str.getBytes().length+" \\n" +
"\\r\\n" + str;
Servlet
servlet接口,定义servlet规范
public interface Servlet
void init() throws Exception;
void destroy() throws Exception;
void service(Request request,Response response) throws Exception;
HttpServlet
处理请求的httpservlet
public abstract class HttpServlet implements Servlet
public abstract void doGet(Response response,Request request);
public abstract void doPost(Request request,Response response);
@Override
public void service(Request request, Response response) throws Exception
if("GET".equals(request.getMethod()))
doGet(response,request);
else
doPost(request,response);
LagouServlet
自定义servlet
public class LagouServlet extends HttpServlet
@Override
public void doGet(Response response, Request request)
try
Thread.sleep(200000);
catch (InterruptedException e)
e.printStackTrace();
String string="<h1>LagouServlet get</h1>";
try
response.outPut(HttpProtocolUtil.getHttpHeader200(string.getBytes().length)+string);
catch (IOException e)
e.printStackTrace();
@Override
public void doPost(Request request, Response response)
String string="<h1>LagouServlet post</h1>";
try
response.outPut(HttpProtocolUtil.getHttpHeader200(string.getBytes().length)+string);
catch (IOException e)
e.printStackTrace();
@Override
public void init() throws Exception
@Override
public void destroy() throws Exception
Request
请求信息封装类
/**
* 把请求信息封装为Request对象(根据inputStream封装)
*/
public class Request
//请求方式get/post
private String method;
//url
private String url;
//输入流,其他属性从输入流解析出来
private InputStream inputStream;
public Request()
//构造器,输入流传入,解析出url和method
public Request(InputStream inputStream) throws IOException
this.inputStream = inputStream;
int count=0;
while (count==0)
count=inputStream.available();
byte[] bytes=new byte[count];
inputStream.read(bytes);
System.out.println("请求信息:"+new String(bytes));
String string=new String(bytes);
//获取第一行请求头信息 GET / HTTP/1.1
String firstLine=string.split("\\\\n")[0];
String[] s = firstLine.split(" ");
this.method=s[0];
this.url=s[1];
System.out.println("method:"+this.method);
System.out.println("url:"+this.url);
public String getMethod()
return method;
public void setMethod(String method)
this.method = method;
public String getUrl()
return url;
public void setUrl(String url)
this.url = url;
public InputStream getInputStream()
return inputStream;
public void setInputStream(InputStream inputStream)
this.inputStream = inputStream;
RequestProcessor
请求处理器
public class RequestProcessor extends Thread
private Socket socket;
private Map<String,HttpServlet> servletMap;
public RequestProcessor()
public RequestProcessor(Socket socket, Map<String, HttpServlet> servletMap)
this.socket = socket;
this.servletMap = servletMap;
@Override
public void run()
try
InputStream inputStream = socket.getInputStream();
//封装request/response对象
Request request=new Request(inputStream);
Response response=new Response(socket.getOutputStream());
if(servletMap.get(request.getUrl())==null)
response.outPutHtml(request.getUrl());
else
HttpServlet httpServlet = servletMap.get(request.getUrl());
httpServlet.service(request,response);
socket.close();
catch (Exception e)
e.printStackTrace();
Response
返回信息封装类
/**
* 把相应信息封装为Response对象(根据outputStream封装)
* 该对象需要提供核心对象,输出静态资源(html)
*/
public class Response
private OutputStream outputStream;
public Response()
public Response(OutputStream outputStream)
this.outputStream = outputStream;
//使用输出流输出指定字符串
public void outPut(String content) throws IOException
this.outputStream.write(content.getBytes());
/**
*
* @param path url ,根据url获取到静态资源的绝对路径,根据绝对路径,进一步读取该静态文件,最终通过输出流输出
*/
public void outPutHtml(String path) throws IOException
//获取静态资源的绝对路径
String absoluteResourcePath=StaticResourceUtil.getAbsolutePath(path);
//输出静态资源
File file=new File(absoluteResourcePath);
if(file.exists()&& file.isFile())
//读取静态资源文件,输出静态资源
StaticResourceUtil.outputStaticResource(new FileInputStream(file),outputStream);
else
//输出404
outPut(HttpProtocolUtil.getHttpHeader404());
StaticResourceUtil
静态资源工具类
public class StaticResourceUtil
/**
* 获取静态资源的绝对路径
* @param path
* @return
*/
public static String getAbsolutePath(String path)
String absolutePath=StaticResourceUtil.class.getResource("/").getPath();
return absolutePath.replaceAll("\\\\\\\\","/")+path;
/**
* 读取静态资源文件输入流,通过输出流输出
* @param inputStream
* @param outputStream
*/
public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException
int count=0;
while (count==0)
count = inputStream.available();
int resourceSize=count;
//http请求头输出,然后输出具体内容
outputStream.write(HttpProtocolUtil.getHttpHeader200(resourceSize).getBytes());
//读取输出内容
long written=0;//已经读取的内容长度
int byteSize=1024;//计划每次缓冲长度
byte[] bytes=new byte[byteSize];
while (written<resourceSize)
if(written+byteSize>resourceSize)//剩余未读取大小不足1024,那就按照真实长度处理
byteSize= (int) (resourceSize-written);
bytes=new byte[byteSize];
inputStream.read(bytes);
outputStream.write(bytes);
outputStream.flush();
written+=byteSize;
以上是关于tomcat学习笔记手写tomcat的主要内容,如果未能解决你的问题,请参考以下文章
阿里P8架构师手写笔记:Spring源码+JVM+MySQL+Kafka+Tomcat
阿里P8架构师手写笔记:Spring源码+JVM+MySQL+Kafka+Tomcat