Java实现能获取静态资源的简易版服务器(类Tomcat)
发布日期:2021-06-30 16:02:29 浏览次数:2 分类:技术文章

本文共 8322 字,大约阅读时间需要 27 分钟。

推荐:

Java实现能获取静态资源的简易版服务器(类Tomcat)

我们平时使用Tomcat做为服务器进行Web开发,通常会输入http://localhost:8888/index.html这种URL,这种请求的Request Headers 如下:

GET /index.html HTTP/1.1Host: localhost:8888Connection: keep-aliveCache-Control: max-age=0Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36Sec-Fetch-User: ?1Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Sec-Fetch-Site: noneSec-Fetch-Mode: navigateAccept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en;q=0.8

Request Headers 可以得到请求想要访问的静态资源是index.html(我们自己实现服务器,浏览器发起请求,服务器后台得到就是Request Headers这种数据 )。

现在我们知道如何获取请求想要的资源了(从Request Headers 获取),这就好办了。

当用户在浏览器中输入URL发起请求,其实就是与服务器先建立网络连接,再获取服务器上的资源。

这就和Java网络编程有关系了,现在说一说服务器主要的任务:

  • 服务器首先要打开ServerSocket,当有客户端(浏览器)进行连接,并且发起请求后,获得该请求的Request Headers (你可以把浏览器当作我们之前实现的多人聊天室里面的客户端,因为浏览器向服务器发起请求,数据也是通过流Stream或者类似的组件实现的)。
  • 服务器获得用户请求的Request Headers 后,然后从Request Headers 中分析出资源名称。
  • 知道用户想要的资源后,服务器将资源与一些必要信息打包发送给客户端(协议、状态码、资源等),当资源不存在时,服务器返回404.html资源。

Bootstrap类,启动服务器的模块。

import connector.Connector;public final class Bootstrap {
public static void main(String[] args) {
Connector connector = new Connector(); connector.start(); }}

Connector类,创建ServerSocket(用于客户端建立连接)、创建Request(从Request Headers 中获取客户端请求的资源)、创建Response(将协议、状态码、资源等数据打包写入流中)、创建StaticProcessor(借助Response将资源与一些必要信息发送给服务器),代码注释应该非常清楚。

package connector;import processor.StaticProcessor;import java.io.Closeable;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;public class Connector implements Runnable {
private static final int DEFAULT_PORT = 8888; private ServerSocket server; private int port; public Connector(){
this(DEFAULT_PORT); } public Connector(int port) {
this.port = port; } public void start(){
new Thread(this).start(); } @Override public void run() {
try {
// 创建ServerSocket,绑定、监听端口 server = new ServerSocket(port); System.out.println("启动服务器,监听端口:" + port); while(true){
// 等待客户端连接 Socket socket = server.accept(); // 获取输入流 InputStream input = socket.getInputStream(); // 获取输出流 OutputStream output = socket.getOutputStream(); // 创建请求request,并且传入输入流(有客户端请求的信息) Request request = new Request(input); // request通过输入流的信息,分析出客户端想要的资源 request.parse(); // 创建响应response,并且传入输出流(方便将获取的资源发送给客户端) Response response = new Response(output); // response需要request的uri(客户端请求的资源) response.setRequest(request); // 创建处理者processor StaticProcessor processor = new StaticProcessor(); // processor通过response把数据发送给客户端 processor.process(response); //关闭socket close(socket); } } catch (IOException e) {
// 浏览器可以识别状态码,当状态码表示请求不成功时(如404),似乎会断开socket,所以这里不进行处理 } finally{
close(server); } } private void close(Closeable closeable){
if(closeable != null){
try {
closeable.close(); } catch (IOException e) {
e.printStackTrace(); } } }}

Request类,从客户端与服务器建立连接的socket的输入流InputStream中读取信息,并且分析出客户端想要的资源。

package connector;import java.io.IOException;import java.io.InputStream;public class Request {
private static final int BUFFER_SIZE = 1024; private InputStream input; private String uri; public Request(InputStream input) {
this.input = input; } public String getUri() {
return uri; } public void parse(){
int length = 0; byte[] buffer = new byte[BUFFER_SIZE]; try {
// 读取流里面的数据,并且记录长度 length = input.read(buffer); } catch (IOException e) {
e.printStackTrace(); } // 将数据转化成StringBuilder StringBuilder request = new StringBuilder(); for (int i = 0; i < length; i++) {
request.append((char) buffer[i]); } // 分析出客户端想要的资源 uri = parseUri(request.toString()); } /** *从 “GET /index.html HTTP/1.1 * ...... * ”中获取index.html * 通过空格来分离出来 * */ public String parseUri(String request){
int index1 , index2; // 第一个空格的位置 index1 = request.indexOf(' '); if(index1 != -1){
// 第二个空格的位置 index2 = request.indexOf(' ', index1+1); if(index2 != -1){
// 分离出资源名称 return request.substring(index1 + 2 , index2); } } // 没有办法解析出uri return ""; }}

Response类,将协议、状态码、资源等数据打包写入流中。

package connector;import java.io.*;public class Response {
private static final int BUFFER_SIZE = 1024; Request request; OutputStream output; public Response(OutputStream output) {
this.output = output; } public void setRequest(Request request) {
this.request = request; } public void sendStaticResource() throws IOException {
try{
// 通过request的uri,获取资源的路径 String filePath = getClass() .getClassLoader() .getResource(request.getUri()).getFile(); // 创建资源文件 File file = new File(filePath.substring(1 , filePath.length())); // 将资源写入流里面,HttpStatus.SC_OK是状态码 write(file , HttpStatus.SC_OK); } catch (Exception e) {
// 当出现错误时,简单处理 ,发送404.html给客户端 String errorFilePath = getClass().getClassLoader().getResource("404.html").getFile(); // 将资源写入流里面,HttpStatus.SC_NOT_FOUND是状态码 write(new File(errorFilePath.substring(1 , errorFilePath.length())) , HttpStatus.SC_NOT_FOUND); } } private void write(File resource , HttpStatus status) throws IOException {
try(FileInputStream fis = new FileInputStream(resource)){
// 先将协议、状态码等必要信息写入流中,ConnectorUtils是工具类 output.write(ConnectorUtils.renderStatus(status).getBytes()); byte[] buffer = new byte[BUFFER_SIZE]; int length = 0; // 把资源文件写入流中 while((length = fis.read(buffer , 0 , BUFFER_SIZE)) != -1){
output.write(buffer , 0 ,length); } } }}

HttpStatus类,状态码枚举类。

package connector;public enum HttpStatus {
SC_OK(200 , "OK"), SC_NOT_FOUND(404 , "File Not Found"); private int statusCode; private String reason; HttpStatus(int statusCode, String reason) {
this.statusCode = statusCode; this.reason = reason; } public int getStatusCode() {
return statusCode; } public String getReason() {
return reason; }}

ConnectorUtils类(工具类),将协议、状态码等信息组装成浏览器可以识别的信息。

package connector;public class ConnectorUtils {
public static final String PROTOCOL = "HTTP/1.1"; public static final String CARRIAGE = "\r"; public static final String NEWLINE = "\n"; public static final String SPACE = " "; public static String renderStatus(HttpStatus status){
StringBuilder sb = new StringBuilder(PROTOCOL) .append(SPACE) .append(status.getStatusCode()) .append(SPACE) .append(status.getReason()) .append(CARRIAGE).append(NEWLINE) .append(CARRIAGE).append(NEWLINE); return sb.toString(); }}

StaticProcessor类,做为静态资源处理者的身份,借助Response进行处理。

package processor;import connector.Response;import java.io.IOException;public class StaticProcessor {
public void process(Response response){
try {
response.sendStaticResource(); } catch (IOException e) {
// 不处理浏览器断开连接等错误 } }}

这里我们便完成了一个Java实现的能获取静态资源的简易版服务器。

项目结构

在这里插入图片描述
项目源码

测试

请求成功,服务器有资源。

在这里插入图片描述
在这里插入图片描述

请求失败,服务器没有资源。

在这里插入图片描述

在这里插入图片描述

大家可以动手试一试,不用ServerSocket来实现,用NIO模型或者AIO模型的组件来实现。

如果有说错的地方,请大家不吝赐教(记得留言哦~~~~)。

转载地址:https://kaven.blog.csdn.net/article/details/104216019 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:String、StringBuffer和StringBuilder的区别
下一篇:使用try-with-resources优雅关闭资源

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年04月09日 19时28分25秒