手写http服务器--2.java基于socket连接实现

目录

一.前言

二.示例

 三.原理

四.逐步实现

1.创建ServletContext容器

2.@WebServlet注解扫描

2.实现socket连接

2.交给JhRequsetHandler处理

3.HttpServletRequset封装请求对象

4.UrlMappingHandler路径映射处理器

5.HttpServlet抽象类

6.HttpServletResponse对象封装

7.JhResponseHandler响应处理器

8.启动方式

五.待优化地方


一.前言

如果你觉得对你有所帮助,别忘了给博主一个赞哟!

本篇我们来正式来编写服务器,我们可以利用socket连接来构建一个简单的服务器

下面我们来实验一下:

二.示例

首先,我们创建一个maven项目,我这里给我的服务器起名为JhWebServer

 这是md文件中的自述,下面我们来看一下架构:

 三.原理

   主要就是通过socket连接, 然后把输入流传给HttpServletRequest对象封装,然后根据路径UrlMappingHandler处理器来判断路径后缀是静态还是动态servlet资源,静态直接通过输出流把文件写回去,动态则调用HttpServlet的service()方法,去执行逻辑业务操作,最后不管是静态还是动态请求,都经过HttpResponseHandler响应处理器对结果进行封装,主要就是响应行,响应头,响应体,然后通过socket输出流写回前端

四.逐步实现

1.创建ServletContext容器

 我们需要先把servletContext容器创建好:

/**
 * servlet容器
 * @author JJH
 */
public class ServletContext {
    /**
     * 每个web应用都要一个名称
     */
    private String appName;

    /**
     * 所有的servlet实例
     * K: 路径
     * v: servlet的字节码对象
     */
    private  Map<String,Class<?>> servletMap = new ConcurrentHashMap<>();
    /**
     * servlet的处理器,负责把所有的servlet注册到map中
     */


   public ServletContext(String appName){
       this.appName = appName;
       this.run();
   }



    /**
     * 将所有被@WebServlet注解标记的类加载到map集合中
     */
   private void run(){
        Map<String, Class<?>> servlets = FileUtils.getServlets();
        this.servletMap.putAll(servlets);
    }


    /**
     * 返回所有的servlet字节码对象集合
     * @return
     */
    public  Map<String,Class<?>> getServlets(){
        return this.servletMap;
    }


    public String getAppName() {
        return appName;
    }
}

 容器中主要放的是所有servlet集合,为了简便,我们使用注解@WebServlet(path)

2.@WebServlet注解扫描

底层就是只要是被这个注解的类,就会被扫描到,然后以path为键,这个类的字节码对象为值存入map集合中:我们来看一下扫描的代码:


/**
 * @author JJH
 */
public class FileUtils {

    /**
     * 返回被@WebServlet注解标注的类的字节码集合
     *
     * @return
     */
    public static Map<String, Class<?>> getServlets() {
          HashMap<String,Class<?>> map = new HashMap<>();

        List<File> list = getClassFileByAnnatation(WebServlet.class);

        for (File file : list) {
            String path = file.getPath();
            String className = getClassName(path);
            Class<?> aClass = null;
            try {
                aClass = Class.forName(className);
                WebServlet annotation = aClass.getAnnotation(WebServlet.class);
                String url = annotation.path();
                map.put(url,aClass);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }

        return map;
    }


    public static String getClassName(String path) {
        String replace = path.replace("\\", ".");
        int index = replace.lastIndexOf("classes");
        String substring = replace.substring(index + 8);
        String classname = substring.substring(0, substring.lastIndexOf("class") - 1);
        return classname;
    }

    public static void listFile(File file, List<File> fileList) {
        if (!file.isDirectory()) {
            fileList.add(file);
        } else {
            File[] files = file.listFiles();
            File[] var3 = files;
            int var4 = files.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                File subFile = var3[var5];
                listFile(subFile, fileList);
            }
        }

    }

    public static List<File> getClassFileByPackage(String[] packagePaths) {
        List<File> fileList = new ArrayList();
        ArrayList<File> classFileList = new ArrayList();
        String[] var3 = packagePaths;
        int var4 = packagePaths.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String packge = var3[var5];
            String replace = packge.replace(".", "/");
            URL url = ClassLoader.getSystemResource(replace);
            String path = url.getPath();
            File file = new File(path);
            listFile(file, fileList);
            Iterator var11 = fileList.iterator();

            while(var11.hasNext()) {
                File fiLe = (File)var11.next();
                String name = fiLe.getName();
                if (name.endsWith(".class")) {
                    classFileList.add(fiLe);
                }
            }
        }

        return classFileList;
    }

    public static List<File> getClassFiles() {
        URL url = Thread.currentThread().getContextClassLoader().getResource("");
        String path = url.getPath();
        File file = new File(path);
        List<File> fileList = new ArrayList();
        listFile(file, fileList);
        ArrayList<File> classFileList = new ArrayList();
        Iterator var5 = fileList.iterator();

        while(var5.hasNext()) {
            File fiLe = (File)var5.next();
            String name = fiLe.getName();
            if (name.endsWith(".class")) {
                classFileList.add(fiLe);
            }
        }

        return classFileList;
    }



    public static List<File> getClassFileByAnnatation(Class<WebServlet> webServletClass) {
        List<File> list = new ArrayList();
        List<File> classFiles = getClassFiles();
        Iterator var3 = classFiles.iterator();

        while(var3.hasNext()) {
            File classFile = (File)var3.next();
            String path = classFile.getPath();
            String className = getClassName(path);
            Class<?> aClass = null;

            try {
                aClass = Class.forName(className);
            } catch (ClassNotFoundException var9) {
                throw new RuntimeException(var9);
            }

            Annotation annotation = aClass.getAnnotation(webServletClass);
            if (annotation != null) {
                list.add(classFile);
            }
        }

        return list;
    }

    /**
     * 返回webapps目录下所有的静态资源
     * @return
     */
    public static List<File> getWebappsStaticFiles(){
        List<File> files = new ArrayList<>();
        List<File> resFiles = new ArrayList<>();
        File file = new File("src/main/webapp");
        listFile(file,files);

        for (File file1 : files) {
            String name = file1.getName();
            if(!name.endsWith("web.xml")){
               resFiles.add(file1);
            }
        }

        return resFiles;
    }

//    public static void main(String[] args) {
//        FileUtils.getWebappsStaticFiles();
//    }

    /**
     * 返回文件的字节数组
     * @param file 文件
     * @return 字节数组
     */
    public static byte[] getFileByte(File file){

        byte[] bytes = new byte[(int)file.length()];

        InputStream in = null;
        try {
            in = new FileInputStream(file);
            in.read(bytes);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                assert in != null;
                in.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
       return bytes;
    }

    /**
     * 获取webapp目录下的某一个文件
     * @param name
     * @return
     */
    public static File getWebAppOneFile(String name){
        List<File> files = getWebappsStaticFiles();
        for (File file : files) {
           if (name.contains(file.getName())){
               return file;
           }
        }
        return null;
    }


}

 可以看到被注解了@WebServlet对象的文件会被返回到map集合中

2.实现socket连接

/**
 * @author JJH
 */
public class ServerSocketHandler {

    private ServletContext context;

    private int port =8080;

    public ServerSocketHandler(ServletContext context){
        this.context = context;
        this.start(this.context);
    }
    public ServerSocketHandler(ServletContext context,int port){
        this.context = context;
        this.port = port;
        this.start(this.context);
    }




    public void start(ServletContext context) {
        ServerSocket serverSocket;
        try {

            serverSocket = new ServerSocket(this.port);
            System.out.println("JHWebServer start success! Listening port: "+this.port);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //换成线程池来处理,更加高效
        ThreadPoolExecutor pool =
                new ThreadPoolExecutor(10,15,30, TimeUnit.SECONDS,new LinkedBlockingDeque<>());

        //只要服务器没有关闭,就一直运行
        while (!serverSocket.isClosed()){
            //接受客户端发来的请求
            Socket socket ;
            try {
                socket = serverSocket.accept();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            //将socket交给线程处理
           // new Thread(new JhRequestHandler(socket)).start();
            pool.submit(new JhRequestHandler(socket,context));
        }

    }

}

这里是我的最终版了,加入了线程池,线程池可以提高效率,提前创建好线程,可以实现复用,

每次来一个请求,就提交任务让他执行就可以了

2.交给JhRequsetHandler处理

下面看一下这个任务类:

public class JhRequestHandler implements Runnable{


    private Socket socket =null;

    private ServletContext context;

    public JhRequestHandler(Socket socket,ServletContext context){
        this.context = context;
        this.socket = socket;
    }


    @Override
    public void run() {

        try {
            //1.获取输入流
            InputStream in = socket.getInputStream();
            OutputStream os = socket.getOutputStream();

            //2.创建俩对象
            HttpServletRequest requset = new HttpServletRequest(in);
            HttpServletResponse response = new HttpServletResponse();


            //3.路径映射
            UrlMappingHandler urlMappingHandler =
                    new UrlMappingHandler(requset,response,context);
            Object result = urlMappingHandler.urlMapping(requset,response,context);
            //处理找不到的情况
            if(result==null){
                response.setStatus(404);
            }else {
                //静态文本资源
                if(result.getClass()==String.class){
                    response.setStatus(200);
                    response.write(result.toString());
                }else if(result.getClass()==File.class){
                    response.setStatus(200);
                    response.write((File) result);
                }
                else{
                    //servlet动态资源
                    HttpServlet servlet = (HttpServlet) result;
                    servlet.service(requset,response);
                    response.setStatus(200);

                }
            }


            //4。对响应在封装
             new JhResponseHandler(response,os);

            //关闭流
            os.flush();
            os.close();
            in.close();
            socket.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                //最终一定要确保socket关闭
                socket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }


    }
}

这个任务类,也就是请求的处理类,做了什么事?我们来分析一下:

(1).获取到socket连接的输入流和输出流

(2)创建HttpServletRequest和HttpServletResponse对象

(3).进行路径映射

(4).用JhResponseHandler类进行响应结果封装并发送响应

3.HttpServletRequset封装请求对象

我们来看一下封装请求对象做了哪些事情?

public class HttpServletRequest extends JhRequset{


    /**
     * 方法类型
     */
    private String method;
    /**
     * 请求路径
     */
    private String url;
    /**
     * 参数列表
     */
    private HashMap<String,String> params = new HashMap<>();
    /**
     * 请求头信息
     */
    private HashMap<String,String> headers = new HashMap<>();
    /**
     * 请求体(json格式)
     */
    private String requestBody;
    /**
     * socket的输入流
     */
    private InputStream inputStream;


    public HttpServletRequest(InputStream inputStream) throws IOException {

        this.inputStream = inputStream;
        this.init(this.inputStream);
    }


    /**
     * 对http请求的封装
     * @param inputStream socket输入流
     */
    private void init(InputStream inputStream) throws IOException {
        try {

            BufferedReader reader =
                    new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
            //读取第一行
            String firstLine = reader.readLine();
            // System.out.println(firstLine);
            String[] s = firstLine.split(" ");
            //设置该请求的方法
            this.method = s[0];
            //设置请求路径
            String allPath = s[1];

            if(allPath.contains("?")){
                //如果包含了?,那么要截取?之前的
                this.url = allPath.substring(0, allPath.indexOf("?"));
            }else {
                this.url = allPath;
            }



            /**
             * 对get请求处理
             */
            if("GET".equals(method)){
                int index = allPath.indexOf("?");
                if (index != -1) {
                    //有参数
                    String params = allPath.substring(index + 1);
                    //判断是否有&号
                    if (params.contains("&")) {
                        String[] parms = params.split("&");
                        for (String parm : parms) {
                            String[] str = parm.split("=");
                            this.params.put(str[0], str[1]);
                        }
                    } else {
                        //就一个参数
                        String[] str = params.split("=");
                        this.params.put(str[0], str[1]);
                    }
                }
            }

            /**
             * 对请求头处理
             */
            String msg;
            while ((msg = reader.readLine()) != null) {
                if (msg.length() == 0) {
                    break;
                }
                String[] headers = msg.split(":");
                String header = headers[0];
                String value = headers[1].trim();
                this.headers.put(header,value);
            }

            /**
             * 对POST请求处理
             */
            if("POST".equals(method)) {
                StringBuffer sb = new StringBuffer();
                String length = this.headers.get("Content-Length");
                int len = Integer.parseInt(length);
                for(int i=0;i<len;i++){
                    char ch= (char) reader.read();
                  /*
                     用一下正则表达式来排除数字,英文字母特殊字符
                     这样这个字符肯定是中文,utf-8中文是三个字符表示的,
                     这时让i=i+2就可以了
                   */
                    String chs = String.valueOf(ch);
                    if (RegexUtil.isChinese(chs)) {
                        i = i+2;
                    }
                    sb.append(chs);
                }
                String body = sb.toString();

                //判断是那种提交格式
                String type = this.headers.get("Content-Type");
                if("application/x-www-form-urlencoded".equals(type)){
                    //表单提交格式,把它添加到参数列表中去
                    if(body.contains("&")){
                        String[] split = body.split("&");
                        for (String parms : split) {
                            String[] p = parms.split("=");
                            this.params.put(p[0],p[1]);
                        }
                    }else {
                        //就一个参数
                        String[] str = body.split("=");
                        this.params.put(str[0],str[1]);
                    }
                } else if (type.contains("application/json")) {
                    //json格式的请求体
                    this.requestBody = body;
                }
            }


        }catch (Exception e){
            e.printStackTrace();
        }

    }


    private void setCharacterEncoding(String encoding){

    }

    /**
     * 返回请求方法
     * @return 请求方法
     */
    public String getMethod() {
        return method;
    }

    /**
     * 返回请求路径
     * @return 请求路径
     */
    public String getUrl() {
        return url;
    }

    /**
     * 返回请求参数,包括get请求和post请求的表单形式
     * @return 请求参数map集合
     */
    public HashMap<String, String> getParams() {
        return params;
    }

    /**
     * 返回psot请求体
     * @return 请求体
     */
    public String getRequestBody(){
        return this.requestBody;
    }

    /**
     * 返回请求头信息
     * @return 请求头信息集合
     */
    public HashMap<String,String> getHeaders(){
        return this.headers;
    }

    /**
     * 根据参数名称返回参数值
     * @param parm 参数名称
     * @return 参数值
     */
    public String getPramater(String parm){
        if (this.params.get(parm) != null) {
            return this.params.get(parm);
        }else{
            return null;
        }
    }




}

为了简单,我们先暂时实现对GET和POST请求的处理,

主要就是判断是哪个请求方式,然后获取请求参数,请求路径,请求体等,然后把它们放到属性里封装起来

4.UrlMappingHandler路径映射处理器

下面我们来看一下路径映射做了哪些事情?

/**
 * 路径映射处理器
 * @author JJH
 */
public class UrlMappingHandler {


    private HttpServletRequest request;

    private HttpServletResponse response;

    private ServletContext context;


    public UrlMappingHandler(HttpServletRequest request, HttpServletResponse response, ServletContext context) {
        this.context = context;
        this.request = request;
        this.response = response;
    }

    /**
     * 路径映射
     *
     * @param request requset对象
     * @return httpservlet对象
     */
    public Object urlMapping(HttpServletRequest request, HttpServletResponse response, ServletContext context) {
        String url = request.getUrl();
        //判断静态资源
        if (RegexUtil.isStaticFiles(url)) {
            return mappingStaticResources(url, response);
        } else {
            HttpServlet[] servletes = new HttpServlet[1];
            //匹配servlet动态资源
            Map<String, Class<?>> servlets = context.getServlets();

            servlets.forEach((path, clazz) -> {
                if (url.equals(path)) {
                    //创建servlet对象
                    try {
                        HttpServlet servlet = (HttpServlet) clazz.newInstance();
                        servletes[0] = servlet;
                    } catch (InstantiationException | IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            return servletes[0];
        }
    }


    /**
     * 匹配静态资源
     */
    public Object mappingStaticResources(String url, HttpServletResponse response) {



        //0表示是字符串形式, 1表示是二进制文件
        int flag = 1;
        if(url.equals("/")){
           //空字符串,就去默认找index.html页面
           url = "/index.html";
           flag = 0;
        }else if (url.endsWith(".html")) {
            flag = 0;
        } else if (url.endsWith(".js")) {
            response.setHeader("Content-Type: application/javascript");
            flag = 0;
        } else if (url.endsWith(".css")) {
            response.setHeader("Content-Type: text/css");
            flag = 0;
        } else if (url.endsWith(".jpeg") || url.endsWith(".jpg")) {
            response.setHeader("Content-Type: image/jpge");
            response.setHeader("Accept-Ranges: bytes");
        } else if (url.endsWith(".webp")) {
            response.setHeader("Content-Type: image/webp");
            response.setHeader("Accept-Ranges: bytes");
        } else if (url.endsWith(".gif")) {
            response.setHeader("Content-Type: image/gif");
            response.setHeader("Accept-Ranges: bytes");
        } else if (url.endsWith(".png")) {
            response.setHeader("Content-Type: image/png");
            response.setHeader("Accept-Ranges: bytes");
        }

        List<File> files = FileUtils.getWebappsStaticFiles();

        if (flag == 0) {
            //文本格式
            BufferedReader in;
            StringBuffer sb = new StringBuffer();
            for (File file : files) {
                if (url.contains(file.getName())) {
                    try {
                        in = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
                        int msg;
                        while ((msg = in.read()) != -1) {
                            sb.append((char) msg);
                        }
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            return sb.toString();
        }else {

          return FileUtils.getWebAppOneFile(url);
        }
    }
}


 可以看到当是静态请求时,将该文件直接写到StringBuffer缓冲区中返回,如果是动态请求,那么会根据请求路径去ServletContext中的servletmap集合中去找对应的servlet键,如果找到,就利用反射创建这个servlet的对象,然后执行service()方法,我们下面来看一下HttpServlet做了哪些事情:

5.HttpServlet抽象类

public abstract class HttpServlet implements Servlet{
    @Override
    public void init(ServletConfig config) {

    }

    @Override
    public void service(JhRequset request,JhResponse respone) {

        HttpServletRequest request1 = (HttpServletRequest) request;
        HttpServletResponse respone1 = (HttpServletResponse) respone;
        String method = request1.getMethod();
        if("GET".equals(method)){
            this.doGet(request1,respone1);
        } else if ("POST".equals(method)) {
            this.doPost(request1,respone1);
        } else if ("OPTIONS".equals(method)) {
            this.doOptions(request1,respone1);
        }

    }

    @Override
    public void destroy() {

    }

    /**
     * 执行对应的get方法
     * @param request
     * @param respone
     */
    public abstract void doGet(HttpServletRequest request, HttpServletResponse respone);


    /**
     * 执行对应的post方法
     * @param request
     * @param respone
     */
    public abstract void doPost(HttpServletRequest request, HttpServletResponse respone);



    /**
     * 执行对应的options方法
     * @param request
     * @param respone
     */
    public void doOptions(HttpServletRequest request, HttpServletResponse respone){



        respone.setHeader("Access-Control-Allow-Origin:*");
        respone.setHeader("Access-Control-Allow-Methods:POST,GET,OPTIONS,DELETE");
        respone.setHeader("Access-Control-Allow-Headers: access-control-allow-origin, authority, content-type, version-info, X-Requested-With");
        respone.setHeader("Access-Control-Allow-Age:3600");
        respone.setStatus(200);
    }

}

 可以看到该抽象类根据请求方式去调用对应的处理方法,至于为什么写成抽象类,那是因为我们的业务servlet有很多,我们只需要继承这个HttpServlet抽象类,就必须实现doGet()和doPost()方法,这样,我们自己的业务就只要关心这两个请求就可以了,参照javaweb。

6.HttpServletResponse对象封装

对应该对象的封装,它其实是一开始就创建好了,然后一直传递,在传递的过程中,把他的属性值都给赋上了,我们来看一下他的属性:

/**
 * @author JJH
 */
public class HttpServletResponse extends JhResponse{

    /**
     * 状态码
     */
    private int status;
    /**
     * 响应头信息
     */
    private HashMap<String,String> headers = new HashMap<>();
    /**
     * 响应体
     * (主要是html,css,json响应体)
     */
    private String responseBody;

    /**
     * 二进制文件
     */
    private File resFile;

    /**
     * 对响应体那部分要写的内容流
     */
    private JhOutputStream resOutputStream;

    public HttpServletResponse(){}





    /**
     * 打印流
     */
    public PrintWriter getWriter(){
        Writer writer = new OutputStreamWriter(this.resOutputStream);
        return new JhPrintwriter(writer,this.responseBody);
    }

    /**
     * 直接写文本文件(html,js,css,json)
     * @param res  字符串
     */
    public void write(String res){
        this.responseBody = res;
    }

    /**
     * 二进制文件
     * @param file 文件
     */
   public void write(File file){
        this.resFile = file;
   }



    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public HashMap<String, String> getHeaders() {
        return headers;
    }

    public void setHeaders(HashMap<String, String> headers) {
        this.headers = headers;
    }


    /**
     * 添加响应头
     * @param header
     */
    public void addHeader(String header){
        String[] str = header.split(":");
        this.headers.put(str[0],str[1]);
    }

    /**
     * 设置响应头,如果有重复的,就覆盖掉
     * @param header
     */
    public void setHeader(String header){
        String[] str = header.split(":");
        this.headers.put(str[0],str[1]);
    }

    /**
     * 根据响应头获取响应头内容
     * @param header 响应头
     * @return 头内容
     */
    public String getHeader(String header){
        AtomicReference<String> str = new AtomicReference<>("");
        this.headers.forEach((head,value)->{
            if(head.equals(header)){
                str.set(value);
            }
        });
        return str.get();
    }

    public String getResponseBody() {
        return responseBody;
    }


    public File getResFile() {
        return resFile;
    }
}

 这里我主要挑了几个常见的重要属性,比如响应头信息,响应体信息还有添加响应头,设置响应头等,至于对字符编码的方法暂时没有做,

7.JhResponseHandler响应处理器

当我们做完以上的流程后,就会到我们的响应处理类中,做最后的封装,下面,我们来看一下它做了哪些事情:

/**
 * 对响应进行处理
 * @author JJH
 */
public class JhResponseHandler {

    private JhResponse response;

    private OutputStream outputStream;


    public JhResponseHandler(JhResponse response,OutputStream outputStream) throws FileNotFoundException {
        this.response = response;
        this.outputStream = outputStream;
        handleResponse(response,outputStream);
    }

    /**
     * 处理响应,可以是所有类型的响应
     * @param response
     * @param outputStream
     */
    private void handleResponse(JhResponse response,OutputStream outputStream) throws FileNotFoundException {
        if(response.getClass()== HttpServletResponse.class){
            //转化为http的响应对象
            HttpServletResponse response1 = (HttpServletResponse) response;

            handleHttpResponse(response1,outputStream);

        }
    }

    /**
     * 处理http的响应
     */
    private void handleHttpResponse(HttpServletResponse response,OutputStream outputStream) throws FileNotFoundException {

        //添加一些统一的响应头
        //设置响应的时间
        response.setHeader("Date:"+ TimeUtil.now());
        response.setHeader("Server:JhWebServer");


        //获取它的状态码,来判断
        int status = response.getStatus();
        if(status==200){
            StringBuffer sb = new StringBuffer();
            //200 OK,返回一个正确结果
            String row = "HTTP/1.1 200 OK\r\n";
            sb.append(row);
            HashMap<String, String> headers = response.getHeaders();
            headers.forEach((head,value)->{
                sb.append(head).append(":").append(value).append("\r\n");
            });
            //判断一下是那种格式
                if(headers.get("Content-Type")!=null) {
                    byte[] bytes;

                    String type = headers.get("Content-Type");

                    if(type.contains("jpg")||type.contains("jpeg")||type.contains("png")
                    ||type.contains("webp")){
                        sb.append("\r\n");

                        File resFile = response.getResFile();
                        byte[] fileByte = FileUtils.getFileByte(resFile);
                        for (byte b : fileByte) {
                            sb.append(b);
                        }
                        try {
                            outputStream.write(sb.toString().getBytes());
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }else {
                        sb.append("\r\n");
                        sb.append(response.getResponseBody());
                         bytes = sb.toString().getBytes();
                        try {
                            outputStream.write(bytes);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }else {
                    //主要是options请求
                    if(!("").equals(response.getHeader("Access-Control-Allow-Headers"))){
                        try {
                            outputStream.write(sb.toString().getBytes());
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }else{
                        //默认没设置响应的格式,那么我们设置为html文本
                        sb.append("Content-Type:text/html; charset=utf-8\r\n");
                        sb.append("Content-Length:").append(response.getResponseBody().length()).append("\r\n");
                        sb.append("\r\n\r\n");
                        sb.append(response.getResponseBody());
                        try {
                            outputStream.write(sb.toString().getBytes());
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }else if (status==404) {
                StringBuffer sb = new StringBuffer();
                String row = "HTTP/1.1 404\r\n";
                sb.append(row);
                HashMap<String, String> headers = response.getHeaders();
                response.setHeader("Content-Type:text/html; charset=utf-8");
                headers.forEach((head,value)->{
                    sb.append(head+":"+value+"\r\n");
                });
                sb.append("\r\n");
                sb.append("<!DOCTYPE html>\n" +
                        "<html lang=\"en\">\n" +
                        "<head>\n" +
                        "    <meta charset=\"UTF-8\">\n" +
                        "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
                        "    <title>NotFound</title>\n" +
                        "</head>\n" +
                        "<body>\n" +
                        "    <h1>404 Not Found</h1>\n" +
                        "</body>\n" +
                        "</html>");
            try {
                outputStream.write(sb.toString().getBytes());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        }


    }









}

 可以看到,主要是对200和404的状态码做了解析,如果是200,那么就对响应进行组装,检查响应的格式,看看是json还是文化,json返回文本,文件返回字节数组等,如果是404,就直接写一个404 Not Found的页面返回就可以了,其他的状态码暂时没做,如果大家有兴趣可以自己实现一下。

8.启动方式

以上就是该服务器的主要流程了,下面让我们来看一下,如何使用该服务器:

/**
 * JhWebServer服务器总体
 * @author JJH
 */
public class JhWebServer {

    private static ServletContext servletContext;

    private static ServerSocketHandler serverSocketHandler;


    /**
     * 直接启动
     * @param appName
     */
    public static ServletContext  run(String appName){
       servletContext = new ServletContext(appName);
       serverSocketHandler = new ServerSocketHandler(servletContext);
       return servletContext;
    }

    /**
     * 修改端口启动
     * @param appName
     * @param port
     */
    public static ServletContext run(String appName,int port){
        servletContext = new ServletContext(appName);
        serverSocketHandler = new ServerSocketHandler(servletContext,port);
        return servletContext;
    }



}

 可以看到该JhWebServer类中有一个静态方法,run(),我们只需要在我们的main方法中写:

JhWebServer.run("应用名称",端口号)

就可以启动运行了

五.待优化地方

该服务器只能实现大致功能,还有很多功能没有实现:

1.Filter过滤器没有实现

2.Cookie和Session存储没有实现

3.其他状态码的解析没有实习

4.PUT,DELETE请求方式没有实现

这些待优化地方以后会逐步优化,如果你觉得对你有所帮助,别忘了给博主一个赞哟!我们下期再见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值