一、实现Response
实现ConnectorUtils
获取资源主目录对应的路径
package connector;import java.io.File;/*** @ClassName:* @Description:* @author: hszjj* @date: 2019/11/26 12:54*/public class ConnectorUtils {public static final String WEB_ROOT =System.getProperty("user.dir") + File.separator + "webroot";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();}}
实现HttpStatus
获取网络状态
package connector;/*** @ClassName:* @Description:* @author: hszjj* @date: 2019/11/26 13:07*/public enum HttpStatus {SC_OK(200,"OK"),SC_NOT_FOUND(404,"File Not Found");private int statusCode;private String reason;HttpStatus(int Code,String reason){this.statusCode=Code;this.reason=reason;}public int getStatusCode() {return statusCode;}public String getReason() {return reason;}}
实现Response
package connector;import java.io.*;/*** @ClassName:* @Description:HTTP/1.1 200 OK* @author: hszjj* @date: 2019/11/26 12:49*/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 {File file = new File(ConnectorUtils.WEB_ROOT, request.getRequestUri());try {write(file, HttpStatus.SC_OK);} catch (IOException e) {write(new File(ConnectorUtils.WEB_ROOT, "404.html"), HttpStatus.SC_NOT_FOUND);}}private void write(File resource, HttpStatus status) throws IOException {try (FileInputStream input = new FileInputStream(resource)) {output.write(ConnectorUtils.renderStatus(status).getBytes());byte[] buffer = new byte[BUFFER_SIZE];int lenth = 0;while ((lenth = input.read(buffer, 0, BUFFER_SIZE)) != -1) {output.write(buffer, 0, lenth);}}}}
二、Response测试
TestUtils
工具类
package test.util;import connector.Request;import java.io.ByteArrayInputStream;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.nio.file.Files;import java.nio.file.Path;import java.nio.file.Paths;/*** @ClassName:* @Description:* @author: hszjj* @date: 2019/11/26 13:43*/public class TestUtils {public static Request createRequest(String requestString){InputStream input=new ByteArrayInputStream(requestString.getBytes());Request request=new Request(input);request.parse();return request;}public static String readFileToString(String filename) throws IOException {return new String(Files.readAllBytes(Paths.get(filename)));}}
ResponseTest
package test.connector;import connector.ConnectorUtils;import connector.Request;import connector.Response;import org.junit.jupiter.api.Assertions;import org.junit.jupiter.api.Test;import test.util.TestUtils;import java.io.ByteArrayOutputStream;import java.io.IOException;/*** @ClassName:* @Description:* @author: hszjj* @date: 2019/11/26 13:36*/public class ResponseTest {private static final String VAliD_REQUEST = "GET /index.html HTTP/1.1";private static final String INVAliD_REQUEST = "GET /notfound.html HTTP/1.1";private static final String STATUS200 = "HTTP/1.1 200 OK\r\n\r\n";private static final String STATUS404 = "HTTP/1.1 404 File Not Found\r\n\r\n";@Testpublic void givenValidRequest_thenReturnStaticResource() throws IOException {Request request = TestUtils.createRequest(VAliD_REQUEST);ByteArrayOutputStream outputStream = new ByteArrayOutputStream();Response response = new Response(outputStream);response.setRequest(request);response.sendStaticResource();Assertions.assertEquals((STATUS200 + TestUtils.readFileToString(ConnectorUtils.WEB_ROOT + request.getRequestUri())),outputStream.toString());}@Testpublic void givenInvalidRequest_thenReturnError() throws IOException {Request request = TestUtils.createRequest(INVAliD_REQUEST);ByteArrayOutputStream outputStream = new ByteArrayOutputStream();Response response = new Response(outputStream);response.setRequest(request);response.sendStaticResource();String resource=TestUtils.readFileToString(ConnectorUtils.WEB_ROOT +"/404.html");Assertions.assertEquals((STATUS404 + resource),outputStream.toString());}}
三、实现Connector和Processor
Processor
处理用户发送的请求,把请求对应的Response准备好
package processor;import connector.Request;import connector.Response;import java.io.IOException;/*** @ClassName:* @Description:* @author: hszjj* @date: 2019/11/26 14:15*/public class StaticProcessor {public void process(Request request, Response response){try {response.sendStaticResource();} catch (IOException e) {e.printStackTrace();}}}
Connector
package connector;import processor.ServletProcessor;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=9999;private ServerSocket server;private int port;private void shutDown(Closeable...closeables){try {for (Closeable shut:closeables){if (shut!=null){shut.close();}}} catch (IOException e) {e.printStackTrace();}}public Connector(int port){this.port=port;}public Connector(){this(DEFAULT_PORT);}public void start(){new Thread(this).start();}@Overridepublic void run() {try {server=new ServerSocket(port);System.out.println("服务器已启动,监听端口【"+port+"】");while (true){Socket socket=server.accept();InputStream input=socket.getInputStream();OutputStream output=socket.getOutputStream();Request request=new Request(input);request.parse();Response response=new Response(output);response.setRequest(request);if (request.getRequestUri().startsWith("/servlet/")){ServletProcessor processor=new ServletProcessor();processor.process(request,response);}else {StaticProcessor processor = new StaticProcessor();processor.process(request, response);}shutDown(socket);}} catch (IOException e) {e.printStackTrace();}}}
服务器主函数Bootstrap
import connector.Connector;/*** @ClassName:* @Description:* @author: hszjj* @date: 2019/11/26 14:29*/public class Bootstrap {public static void main(String[] args) {Connector connector=new Connector();connector.start();}}
四、测试服务器
启动客户端
package test;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;/*** @ClassName:* @Description:* @author: hszjj* @date: 2019/11/26 14:33*/public class TestClient {public static void main(String[] args) {try {Socket socket=new Socket("localhost",9999);OutputStream outputStream=socket.getOutputStream();outputStream.write("GET /index.html HTTP/1.1".getBytes());socket.shutdownOutput();InputStream inputStream=socket.getInputStream();byte[] buffer=new byte[2048];int length=inputStream.read(buffer);StringBuilder response=new StringBuilder();for (int j=0;j<length;j++){response.append((char)buffer[j]);}System.out.println(response.toString());socket.shutdownInput();socket.close();} catch (IOException e) {e.printStackTrace();}}}
结果
效果查看
五、实现ServletRequest和ServletResponse
如图使Request继承ServletRequest接口和重写所有方法。
同理,对Response做出同样的修改继承自ServletResponse。
其中,大部分函数都仅仅用默认修改即可,不过要对Response中的getWriter进行改写,方便动态资源写入进行便捷:
@Overridepublic PrintWriter getWriter() throws IOException {//自动FlushPrintWriter writer=new PrintWriter(output,true);return writer;}
六、实现Servlet
动态资源的Servletpublic class TimeServlet implements Servlet
主要的Service函数如下:
@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {PrintWriter writer=servletResponse.getWriter();writer.println(ConnectorUtils.renderStatus(HttpStatus.SC_OK));writer.println("what time is it now?");writer.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));}

