概述:
我们来缕清 浏览器发送请求,然后服务器响应浏览器 到底经历了什么
- 浏览器发出HTTP请求,Tomcat中的Web服务器负责接收解析,并创建请求和响应对象(request、response)
- 若无Servlet映射,则可直接访问解析的资源,把资源封装到response并返回到Web服务器,Web服务器将信息拆解成HTTP响应返回给浏览器显示
- 若有Servlet映射,则去web.xml查询对应的Servlet路径,并将请求、响应传输给对应的Servlet对象,处理完逻辑后,把信息封装到response返回给Web服务器拆解,然后响应给浏览器显示
- 若既无资源,也无Servlet映射则返回404页面
一、项目结构
二、Request
public class Request {
// 请求地址
private String url;
// 请求方法
private String method;
// 构造函数,参数为后面2.4中Socket建立的IO流
public Request(InputStream in) throws IOException {
// IO读取请求
// 这里踩坑、因为http/1.1是长连接,所以浏览器未超时是不会主动关闭的
// 不能使用循环来读取数据,因为读取不了-1(未主动关闭)
byte[] bytes = new byte[1024];
int length = in.read(bytes);
String str = new String(bytes,0,length);
// 取请求的第一行(具体请求信息请看序文中的HTTP知识铺垫)
String strFirst = str.split("\n")[0];
// 按空格分割
String[] arr = strFirst.split(" ");
// 从第一行中获取方法名和请求地址
method = arr[0];
url = arr[1];
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
}
三、Response
public class Response {
private OutputStream out;
public Response(OutputStream out) {
super();
this.out = out;
}
// 该方法前面的步骤都是为了写好响应头,最后一句话才是写入响应内容
public void write(String content, int statusCode) throws IOException {
out.write( ("HTTP/1.1 " + statusCode + " OK\n").getBytes() );
out.write("Content-Type:text/html;Charset=utf-8\n".getBytes());
out.write("\n".getBytes());
out.write(content.getBytes("UTF-8")); // 这里处理编码问题
}
}
四、Servlet
public abstract class Servlet {
// 类似于HttpSerlvet
public void service(Request request, Response response) {
if(request.getMethod().equalsIgnoreCase("POST")) {
doPost(request, response);
}else if(request.getMethod().equalsIgnoreCase("GET")) {
doGet(request, response);
}
}
// 分别处理POST和GET请求
public abstract void doPost(Request request, Response response);
public abstract void doGet(Request request, Response response);
}
五、Server(重点)
public class Server {
// 资源根目录
public static String WEB_ROOT = System.getProperty("user.dir") + "\\WebRoot";
// 请求的资源地址
public static String url = "";
// 读取web.properties,保存映射关系
private static HashMap<String,String> map = new HashMap<String,String>();
// 静态代码块,加载时运行一次
static {
try {
// 将映射地址存到map集合中
Properties prop = new Properties();
prop.load(new FileInputStream(WEB_ROOT + "\\WEB-INF\\web.properties"));
Set set = prop.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
String key= (String) iterator.next();
String value = prop.getProperty(key);
map.put(key,value);
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 开启服务器
public void start() {
try {
System.out.println("MyTomcat is starting... \n");
// 监听8080端口
ServerSocket serverSocket = new ServerSocket(8080);
// 后期改成NIO,Tomcat默认NIO模式,目前使用BIO (阻塞IO,并不使用多线程了)
while(true){
// 监听客户端连接
Socket socket = serverSocket.accept();
// 由Tomcat服务器来创建请求响应对象
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
Request request = new Request(in);
System.out.println("请求地址:" + request.getUrl());
Response response = new Response (out);
System.out.println("一个请求连接了");
// 分派器
dispatch(request, response);
// 关闭各种资源
in.close();
out.close();
socket.close();
System.out.println("一个请求关闭连接了 \n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 负责指派去哪访问
private void dispatch(Request request, Response response) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
int length = 0;
byte[] bytes = new byte[1024];
FileInputStream fileInputStream = null;
StringBuffer stringBuffer = new StringBuffer();
// 有Servlet映射
if( map.containsKey(request.getUrl().replace("/", ""))){
String value = map.get(request.getUrl().replace("/", ""));
// 反射
// Class clazz = Class.forName("com.xxx.servlet.LoginServlet");
Class clazz = Class.forName(value);
Servlet servlet = (Servlet) clazz.newInstance();
servlet.service(request, response);
// 访问静态资源
}else{
File file = new File(WEB_ROOT,request.getUrl());
// 静态资源存在
if(file.exists()){
fileInputStream = new FileInputStream(file);
while( (length = fileInputStream.read(bytes)) != -1 ){
stringBuffer.append(new String(bytes,0,length));
}
response.write(stringBuffer.toString(),200);
// 静态资源不存在
}else{
file = new File(WEB_ROOT,"/404.html");
fileInputStream = new FileInputStream(file);
while( (length = fileInputStream.read(bytes)) != -1 ){
stringBuffer.append(new String(bytes,0,length));
}
response.write(stringBuffer.toString(),404);
}
}
}
}
六、Start
public class Start {
public static void main(String[] args) {
Server webServer = new Server();
webServer.start();
}
}
七、web.properties
# url mapping = class
LoginServlet = com.xxx.servlet.LoginServlet
八、测试
http://localhost:8080/index.html
http://localhost:8080/LoginServlet
http://localhost:8080/test