• 2021.05.28 手写简易web服务器


    前言

    已经做java开发好几年了,但是一直觉得web服务器是个很神秘的东西,之前有参考过一个大佬用netty写的简易版的springboot,但是最后不了了之了。今天,突然又有了想写个web服务器了,所以就有了今天个小demo

    但是demo还是有点问题,就是浏览器请求的时候,服务端无法响应,但是把浏览器请求终止掉,服务端开始响应了,但是这时候响应结果已经无法传给浏览器了。这个问题,后期得研究下。

    开整

    今天才真正知道并理解了Tomcat是基于socket实现的,以前可能看到过相关博客和文档,但是一直无法正在理解,想不明白它到底是咋样通过Socket实现的,直到今天写完这个demo,我发现突然就想明白了,顿悟的感觉,所以我已经决定自己要动手写一个web框架,名字就叫syske-bootflag先立起来。

    服务端

    服务端其实就是一个socket服务端,用于处理客户端请求。

    public class SyskeCatServer {
        private static final int port = 8080;
        public static void main(String[] args) throws Exception{
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(port);
                System.out.println("SyskeCatServer is starting……");
                while (true){
                    Socket accept = serverSocket.accept();
                    InputStream inputStream = accept.getInputStream();
                    OutputStream outputStream = accept.getOutputStream();
                    SimpleHttpServer simpleHttpServer = new SimpleHttpServer();
                    SyskeRequest syskeRequest = new SyskeRequest(inputStream);
                    SyskeResponse syskeResponse = new SyskeResponse(outputStream);
                    simpleHttpServer.doService(syskeRequest, syskeResponse);
                    accept.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                serverSocket.close();
            }
        }
    }
    

    首先,从serverSocket中分别获取输入流和输出流,这里的SyskeRequest就是对InputStream的封装,SyskeResponse就是对OutputStream的封装。我们根据这个也可以大胆推测,tomcatRequestResponse也是类似的实现。

    doService方法

    class SimpleHttpServer {
    
        public void doService(SyskeRequest request, SyskeResponse response) throws Exception {
            try {
                byte[] bytes = new byte[1024];
                int read;
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                while ((read = request.getInputStream().read(bytes)) != -1) {
                    byteArrayOutputStream.write(bytes);
                }
                byte[] toByteArray = byteArrayOutputStream.toByteArray();
                String requestStr = new String(toByteArray);
                System.out.println(String.format("请求参数:%s", requestStr));
                String[] split = requestStr.split("
    ");
                System.out.println("end");
                response.write("hello syskeCat");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    SyskeRequest

    这里就是包装了InputStream

    public class SyskeRequest {
        private InputStream inputStream;
    
        public InputStream getInputStream() {
            return inputStream;
        }
    
        public SyskeRequest(InputStream inputStream) {
            this.inputStream = inputStream;
        }
    }
    

    SyskeResponse

    这里就是对OutputStream简单包装

    public class SyskeResponse {
            private OutputStream outputStream;
    
            public SyskeResponse(OutputStream outputStream) {
                this.outputStream = outputStream;
            }
    
            //将文本转换为字节流
            public void write(String content) throws IOException {
                StringBuffer httpResponse = new StringBuffer();
                httpResponse.append("HTTP/1.1 200 OK
    ")      //按照HTTP响应报文的格式写入
                        .append("Content-Type:text/html
    ")
                        .append("
    ")
                        .append("<html><head><link rel="icon" href="data:;base64,="></head><body>")
                        .append(content)          //将页面内容写入
                        .append("</body></html>");
                outputStream.write(httpResponse.toString().getBytes());      //将文本转为字节流
                outputStream.close();
            }
    }
    

    测试

    好了,上面这些就是核心代码了,是不是很简单。我们来运行下看看,直接运行main方法就行了。服务端启动了:

    浏览器访问下看看:

    这里浏览器一直没响应,但是后台也没有收到请求,但是当我终止请求后(点击浏览器的x),服务端收到了请求:

    看下后端收到的socket请求参数,是不是很熟悉:

    请求参数:GET /tfgdfgdf HTTP/1.1
    Host: localhost:8080
    Connection: keep-alive
    Cache-Control: max-age=0
    sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"
    sec-ch-ua-mobile: ?0
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    Sec-Fetch-Site: none
    Sec-Fetch-Mode: navigate
    Sec-Fetch-User: ?1
    Sec-Fetch-Dest: document
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0.9
    Cookie: NMTID=00O3GJZgJXpkp8vI0cpmNRliUnGvYkAAAF4sPsi2w
    

    这不就是浏览器的请求头吗?里面包括了请求地址、以及客户端的其他信息。

    如果你想实现更加请求地址响应的需求,只需要解析下请求数据就行了,我觉得这些都简单了,最难的是从01,就是浏览器可以请求后可以收到响应,我觉得我现在做到了0.5,后面的0.5还需要再研究下。

    总结

    整体来说,这个实例还是比较简单的,虽然最后没有完整地达到了最终的目的,但是还是有收获的,至少服务请求收到了吧,所以从这个点上将,收获还是大于预期的,而且对我而言还发现新大陆,只要这个缺憾,后面再好好摸索下,反正这个硬砍我啃定了。

  • 相关阅读:
    【转】nginx(一) nginx详解
    美好生活需要全方位的保障
    Oracle私网mtu滚动修改实施方案
    小知识:后台执行Oracle创建索引免受会话中断影响
    小知识:Oracle RAC添加服务名实现单节点访问
    Windows平台安装Oracle11.2.0.4客户端报错INS-30131
    小知识:如何赋予用户查看所有存储过程和触发器的权限
    小知识:如何判定crontab任务的执行频度
    小知识:解决EXP-00003的报错
    Oracle删除索引规范
  • 原文地址:https://www.cnblogs.com/caoleiCoding/p/14826011.html
Copyright © 2020-2023  润新知