minicat 1.0我们实现了返回固定的字符串"Hello minicat"。
minicat 2.0需求:
封装Request和Response对象,返回html静态资源文件。
封装Request对象
想要封装Request对象,得要先知道请求信息里面有哪些数据,所以我们先打印请求信息。
/**
* minicat启动需要初始化展开的一些操作
*/
public void start() throws IOException {
// 监听端口
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("---->>>minicat start on port:"+port);
//minicat 1.0
// while (true) {
// String data = "Hello minicat!";
// Socket socket = serverSocket.accept();
// // 有了socket,接收到请求,获取输出流
// OutputStream outputStream = socket.getOutputStream();
// String responseText = HttpProtocolUtil.getHttpHeader200(data.getBytes().length)+data;
// outputStream.write(responseText.getBytes());
// socket.close();
// }
// minicat 2.0
while (true) {
Socket socket = serverSocket.accept();
// 获取输入流
InputStream inputStream = socket.getInputStream();
// 因为网络请求会有间断性,所以需要确认数据流必须存在
int count = 0;
while (count == 0) {
// 从输入流中获取请求信息
count = inputStream.available();
}
byte[] bytes = new byte[count];
// 读取数组
inputStream.read(bytes);
System.out.println("====>>> 请求信息:"+new String(bytes));
socket.close();
}
}
打印信息如下:
====>>> 请求信息:GET / HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36
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: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
根据打印出来的请求信息,找到重点要封装的地方 “GET / HTTP/1.1” 。
- “GET”请求方式需要封装,因为后面会根据不同的请求方式找不同的servlet。
- GET后面的“/”也需要封装,这代表的是请求路径。
- “HTTP/1.1”这里不需要要封装,tomcat默认就用HTTP/1.1。
定义Request对象
经过分析,我们需要封装请求方式和请求路径,所以Request定义如下:
/**
* 把请求信息封装成Request对象(根据inputStream输入流封装)
*/
public class Request {
// 请求方式,GET/POST
private String method;
// 请求路径,例如:/,/index.html
private String url;
// 输入流,其他属性从输入流中解析出来
private InputStream inputStream;
// getter setter...
}
在Request实体中增加构造方法,给method和url赋值
public Request() {
}
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);
// 转换成String
String inputStr = new String(bytes);
//获取第一行请求信息
String firstLineStr = inputStr.split("\\n")[0]; // GET / HTTP/1.1
String[] strings = firstLineStr.split(" ");
// 请求方式
this.method = strings[0];
// 请求路径
this.url = strings[1];
}
封装Response对象
Response对象中需要做的事情,根据url获取静态资源绝对路径,进一步根据绝对路径获取静态资源,最终通过输出流输出
获取静态资源文件的绝对路径
public class StaticResourceUtil {
public static String getAbsolutePath(String path) {
// 获取绝对路径
String absolutePath = StaticResourceUtil.class.getResource("/").getPath();
return absolutePath.replaceAll("\\\\","/")+path;
}
}
根据绝对路径获取静态资源
File file = new File(absoluteResourcePath);
通过输出流输出
public class StaticResourceUtil {
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);
written+=byteSize;
}
}
}
Response完整代码如下:
/**
* 封装Response对象,需要依赖于OutputStream
*
* 该对象需要提供核心方法,输出html
*/
public class Response {
private OutputStream outputStream;
public Response() {
}
public Response(OutputStream outputStream) {
this.outputStream = outputStream;
}
/**
* 使用输出流输出指定字符串
* @param content
* @throws IOException
*/
public void output (String content) throws IOException {
outputStream.write(content.getBytes());
}
/**
* 根据url获取静态资源绝对路径,进一步根据绝对路径获取静态资源,最终通过输出流输出
* @param path
*/
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());
}
}
}
V2.0测试
启动类中更改如下
/**
* minicat启动需要初始化展开的一些操作
*/
public void start() throws IOException {
// 监听端口
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("---->>>minicat start on port:"+port);
//minicat 1.0
// while (true) {
// String data = "Hello minicat!";
// Socket socket = serverSocket.accept();
// // 有了socket,接收到请求,获取输出流
// OutputStream outputStream = socket.getOutputStream();
// String responseText = HttpProtocolUtil.getHttpHeader200(data.getBytes().length)+data;
// outputStream.write(responseText.getBytes());
// socket.close();
// }
// minicat 2.0
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();
}
}
/**
* minicat 的程序启动入口
* @param args
*/
public static void main(String[] args) {
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.start();
} catch (IOException e) {
e.printStackTrace();
}
}
在resources目录下增加index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>静态资源</title>
</head>
<body>
Hello minicat 静态资源
</body>
</html>
启动效果如下: