1.V10(版本)
在index页面上显示一张图片 实现: 1:准备一张图片,取名为logo.png 2:在index.html页面上使用<img>标签将其显示出来 3:启动服务端,使用浏览器请求该页面查看图片的显示情况. 测试发现图片无法加载. 原因: 当页面上需要加载其他资源(图片,样式文件,脚本文件等)时,浏览器会自动发起请求去下载 这些资源.因此当我们的页面上有其他资源时,呈现一个页面一问一答就不够了,需要多次的 请求响应来完成. 解决: 将WebServerApplication的start方法中的代码加上死循环,重复接收多次客户端的 连接即可.
1.1项目目录
1.2Java页面
1.2.1ClientHandler
package com.webserver.core;
import com.webserver.http.HttpServletRequest;
import com.webserver.http.HttpServletResponse;
import java.io.*;
import java.net.Socket;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* 该线程任务是负责与一个客户端进行一次HTTP交互
* 浏览器与服务端交互组从一问一答的原则。因此服务端处理一次HTTP交互,步骤如下:
* 1:解析请求(接受浏览器发送过来的请求内容)
* 2:处理请求(根据浏览器发送的请求理解其意图并进行对应的处理)
* 3:发送响应(将处理结果发送给浏览器)
*
*
*/
public class ClientHandler implements Runnable{
private Socket socket;
public ClientHandler(Socket socket){
this.socket = socket;
}
public void run() {
try {
//1解析请求
HttpServletRequest request = new HttpServletRequest(socket);
HttpServletResponse response = new HttpServletResponse(socket);
//2处理请求
DispatcherServlet servlet = new DispatcherServlet();
servlet.service(request,response);
//3发送响应
response.response();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//按照HTTP协议要求,一次交互后要断开连接
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
1.2.2DispatcherServlet
package com.webserver.core;
import com.webserver.http.HttpServletRequest;
import com.webserver.http.HttpServletResponse;
import java.io.File;
import java.net.URISyntaxException;
/**
* 负责处理请求环节
*/
public class DispatcherServlet {
private static File rootDir;
private static File staticDir;
static{
try {
rootDir = new File(
ClientHandler.class.getClassLoader()
.getResource(".").toURI()
);
staticDir = new File(rootDir,"static");
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
public void service(HttpServletRequest request, HttpServletResponse response){
String path = request.getUri();
File file = new File(staticDir,path);
System.out.println("资源是否存在:"+file.exists());
if(file.isFile()){
//将请求的实际文件设置到response的正文上等待响应
response.setContentFile(file);
}else{
file = new File(staticDir,"/root/404.html");
//将404内容设置到response上等待响应
response.setStatusCode(404);
response.setStatusReason("NotFound");
response.setContentFile(file);
}
}
}
1.2.3WebServerApplication
package com.webserver.core;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* WebServer主类
* WebServer是一个Web容器,实现了Tomcat的基础功能。
* 通过本次项目我们的目标是:
* 1:理解Tomcat底层工作原理
* 2:理解HTTP协议的规定
* 3:理解SpringBoot
*/
public class WebServerApplication {
private ServerSocket serverSocket;
/**
* 初始化WebServer
*/
public WebServerApplication(){
try {
System.out.println("正在启动服务端...");
/*
当端口被其他程序占用时,这里会抛出异常:
java.net.BindException:address already in use:JVM
解决:
1:杀死该java进程(推荐)
2:重启电脑(不推荐)
3:更换端口(通常重启后还不行,说明被其他程序占用该端口了)
*/
serverSocket = new ServerSocket(8088);
System.out.println("服务端启动完毕!");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 服务端开始工作的方法
*/
public void start(){
try {
while(true) {
System.out.println("等待客户端连接...");
Socket socket = serverSocket.accept();
System.out.println("一个客户端连接了!");
//启动一个线程处理该客户端交互
ClientHandler clientHandler = new ClientHandler(socket);
Thread thread = new Thread(clientHandler);
thread.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
WebServerApplication webServerApplication = new WebServerApplication();
webServerApplication.start();
}
}
1.2.4HttpServletRequest
package com.webserver.http;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
/**
* 请求对象
* 该类的每一个实例用于表示客户端发送过来的一个HTTP请求
* 每个请求由三部分构成:
* 请求行,消息头,消息正文
*/
public class HttpServletRequest {
private Socket socket;
//请求行的相关信息
private String method;//请求方式
private String uri;//抽象路径
private String protocol;//协议版本
//用一个Map保存所有的消息头,其中key:消息头名字,value:消息头的值
private Map<String,String> headers = new HashMap<>();
public HttpServletRequest(Socket socket) throws IOException {
this.socket = socket;
//1.1解析请求行
parseRequestLine();
//1.2解析消息头
parseHeaders();
//1.3解析消息正文
parseContent();
}
//解析请求行
private void parseRequestLine() throws IOException {
String line = readLine();
System.out.println(line);
//将请求行按照空格拆分为三部分,并赋值给三个变量
String[] data = line.split("\\s");
method = data[0];
uri = data[1];
protocol = data[2];
//测试路径:http://localhost:8088/myweb/index.html
System.out.println("method:"+method);//method:GET
System.o