1.项目架构
2.启动类
在启动类中初始化配置信息和线程池信息,然后创建ServerSocket并使用socket.accept()阻塞等待http请求。
public static void main(String[] args) {
BootStrap bootStrap = new BootStrap();
try {
bootStrap.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void start() throws Exception {
//初始化servlet
loadServlet();
//初始化server配置
loadServer();
// ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 15, 2, TimeUnit.MINUTES, new ArrayBlockingQueue<>(10));
//初始化线程池
ThreadPoolExecutor poolExecutor = ThreadPollConfig.getThreadPool(parameterConfig);
//创建socket监听端口
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("服务器启动,绑定" + port + "端口");
while (true) {
synchronized (lock) {
//监听
Socket socket = serverSocket.accept();
//处理socket
RequestProcessor requestProcessor = new RequestProcessor(socket, httpServletMap);
poolExecutor.execute(requestProcessor);
}
}
}
3.请求处理
该部分负责封装Request对象和Response对象,并判断请求路径是动态资源还是静态资源,来使用对应的类进行处理。
public void run() {
try {
//监听端口
// System.out.println("线程" + Thread.currentThread().getName() + "收到请求");
//接收请求流
InputStream inputStream = socket.getInputStream();
//处理请求流数据,封装成request对象
Request request = handleRequest(inputStream);
if (request == null) return;
//处理输出流数据,封装成response对象
OutputStream outputStream = socket.getOutputStream();
Response response = new Response(outputStream);
String url = request.getUrl();
//判断请求路径是否有servlet来匹配
String match = match(url);
if (!"".equals(match)) {
//交给对应的servlet来处理
HttpServlet httpServlet = httpServletMap.get(match);
httpServlet.init();
httpServlet.service(request, response);
httpServlet.destroy();
} else {
//处理静态资源
response.writeResource(url);
}
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
try {
socket.close();
// System.out.println("线程" + Thread.currentThread().getName() + "socket已关闭");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
4.处理动态资源
创建抽象类Servlet和HttpServlet类
public interface Servlet {
void init() throws Exception;
void destroy() throws Exception;
void service(Request request, Response response) throws Exception;
}
public abstract class HttpServlet implements Servlet {
protected abstract void doGet(Request request, Response response);
protected abstract void doPost(Request request, Response response);
@Override
public void init() throws Exception {
}
@Override
public void destroy() throws Exception {
}
@Override
public void service(Request request, Response response) {
//请求分发
if ("GET".equals(request.getMethod())) doGet(request, response);
else doPost(request, response);
}
}
自定义Servlet类继承HttpServlet类并重写doGet和doPost方法。
public class MyServlet extends HttpServlet {
@Override
protected void doGet(Request request, Response response) {
try {
response.writeAndFlush("MyServlet--get请求" + request.getUrl());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void doPost(Request request, Response response) {
try {
response.writeAndFlush("MyServlet--post请求");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
5.处理静态资源
解析文件的绝对路径并判断文件是否存在
public void writeResource(String url) throws IOException {
//获取文件真实路径
String path = this.getClass().getResource("/").getPath() + url;
//判断是否存在该文件
File file = new File(path);
if (file.exists() && file.isFile()) {
String suffix = path.substring(path.lastIndexOf('.'));
//解析文件
DefaultResourceResolver defaultResourceResolver = DefaultResourceResolver.getInstance();
defaultResourceResolver.resolve(file, this, SuffixResolver.suffixResolver(suffix));
}else {
write404();
}
}
使用流解析文件,注意outputStream不能在此处关闭,否则会将socket的资源一起释放
public void resolve(File file, Response response, String contentType) throws IOException {
InputStream inputStream = Files.newInputStream(file.toPath());
int len = 0;
while (len == 0) {
len = inputStream.available();
}
//先写入请求头
ResponseHeader responseHeader = new ResponseHeader.Builder()
.statusCode(HttpStatusCode.SUCCESS)
.contentType(contentType)
.contentLength(String.valueOf(len))
.build();
response.write(responseHeader.toString());
OutputStream outputStream = response.getOutputStream();
byte[] bytes = new byte[bufferSize];
while ((len = inputStream.read(bytes)) != -1) {
//每次读取的数据都实时写入response中
if (len < bufferSize) {
for (int i = 0; i < len; i++) {
outputStream.write(bytes[i]);
}
} else {
outputStream.write(bytes);
}
}
inputStream.close();
response.flush();
}
6.结果图
7.注意
所有的response必须先写入请求头信息,否则无法成功在浏览器响应。