• 手写一个简单的web服务器(B/S架构)


    涉及Web服务器的相关技术

    IO流、Socket网络编程、HTTP协议、文件、多线程(同时处理多个浏览器的请求)、List集合

    1 web应用服务器运行原理:

    • 连接过程:Web服务器与其浏览器之间建立的连接,判定两者之间是否连接成功;若用户可以找到并打开虚拟文件套接字,说明Web服务器与其浏览器之间成功建立了连接。

    • 请求过程:Web浏览器利用socket文件向其服务器发出各种请求。

    • 获取请求资源的地址

    • 根据是静态资源还是动态资源做出相应的处理

    • 响应过程:在请求过程中发出的请求通过使用HTTP协议传输到Web服务器,然后执行任务处理。

    • 关闭连接:响应过程完成后,Web服务器与其浏览器断开连接的过程。

    2 代码展示

    (1)Main类

    package com.weh;
    import com.weh.httpserver.MyHttpServer;
    public class Main {
    //    开启多线程
        public static void main(String[] args) {
            new Thread(new MyHttpServer()).start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (2)MyHttpServer类

    package com.weh.httpserver;
    import com.weh.response.MyResponse;
    import com.weh.request.MyRequest;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    /*
    * 1.通过Runnable接口的实现类,来开启线程
    * 2.创建并开启服务器
    * 3.实现服务端与客户端进行连接通信
    * */
    public class MyHttpServer implements Runnable{
        public static final int port = 8080;    //服务器端口号
        public static final String WEB_ROOT=System.getProperty("user.dir")+
                File.separator+ "webapps";      //服务器的当前文件目录路径
    
        @Override
        public void run() {
            ServerSocket serverSocket=null;
            Socket socket;
            InputStream input=null;
            OutputStream output=null;
            try {
                System.out.println("Web服务器已开启");
                serverSocket = new ServerSocket(port); //传入端口号并创建服务器
    
            } catch (Exception e) {
                System.exit(0);
            }
            //服务端与客户端浏览器进行通信
            while(true){
                try {
                    socket=serverSocket.accept();//请求
                    input=socket.getInputStream();//获取客户端发过来信息
                    output=socket.getOutputStream();
                    MyRequest request=new MyRequest(input); //数据处理
                    request.parse();
                    MyResponse response=new MyResponse(output);
                    response.setMyRequest(request);
                    response.sendStaticResource();//发送
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }finally {
                    //    释放资源
                    release(input,output);
                }
            }
        }
    //    释放资源
        public void release(InputStream inputStream,OutputStream outputStream){
            try {
                if(inputStream != null) {
                    inputStream.close();
                }
                if(outputStream != null){
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    (3)MyRequest类

    package com.weh.request;
    
    import java.io.InputStream;
    /*
     * 自定义MyRequest类
     * 1.主要处理客户端发过来的信息进行处理并输出
     * 2.获取客户端切换到哪个文件的名称
     * */
    public class MyRequest {
        private InputStream input;
        private String uri;
    
        public MyRequest(InputStream input){
            this.input=input;
        }
    
    //    输出客户端发过来的信息
        public void parse(){
            StringBuffer request=new StringBuffer(2048);
            int len;
            byte[] buffer=new byte[2048];
            try {
                len=input.read(buffer);
            } catch (Exception e) {
                len=-1;
            }
            for(int j=0;j<len;j++){
                request.append((char)buffer[j]);
            }
            System.out.print(request.toString());
            parseUri(request.toString());
        }
    
    //  获取切换后的文件名
        public void parseUri(String srt){
            int index1,index2;
            index1=srt.indexOf(" ");
            if(index1!=-1){
                index2=srt.indexOf(" ",index1+1);
                if(index2>index1){
                    setUri(srt.substring(index1+1,index2));
                }
            }
        }
    
        public void setUri(String uri) {
            this.uri = uri;
        }
    
        public String getUri(){
            return this.uri;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    (4)MyResponse类

    package com.weh.response;
    import com.weh.httpserver.MyHttpServer;
    import com.weh.request.MyRequest;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.util.Arrays;
    import java.util.List;
    
    /*
     * 自定义MyResponse类
     * 1.向Socket的输出流中写请求给浏览器作出响应的结果
     * 2.获取当前目录下的所有文件并创建文件类型的数组
     * 3.数据的读取及转发
     * */
    public class MyResponse {
        private static final int BUFFER_SIZE=1024;
        MyRequest request;
        OutputStream output;
        FileInputStream fis=null;
        PrintWriter printWriter =null;
        public MyResponse(OutputStream output){
            this.output=output;
        }
        public void setMyRequest(MyRequest request){
            this.request=request;
        }
        public void sendStaticResource()throws IOException{
            byte[] bytes=new byte[BUFFER_SIZE];
            try {
                printWriter = new PrintWriter(output); //创建缓冲字符输出流
                File file=new File(MyHttpServer.WEB_ROOT+request.getUri());
                if(file.exists()){  //判断文件目录是否存在
                    //向Socket的输出流中写请求给浏览器作出响应的结果
                    printWriter.println("HTTP/1.1 200 OK");
                    printWriter.println("Content-Type:text/html;charset=UTF-8");
                    printWriter.println();
    //                判断文件是否为普通文件
                    if(file.isFile()){
                        fis=new FileInputStream(file.getPath());    //创建文件字节输入流
                        fis.read(bytes);  //读取直接流
                        printWriter.write(new String(bytes));
    //                判断文件是否为文件目录
                    }else if(file.isDirectory()){
                        List<File> list= Arrays.asList(new File(file.getPath()).listFiles());//获取当前目录下的所有文件并创建文件类型的数组
                        for(File filename : list){//数组遍历
                          if(filename.getName().indexOf(".htm")>0) {//判断是否含有.htm的字符的文件
                              printWriter.write("
  • "); printWriter.write("" + filename.getName() + ""); printWriter.write("
  • "
    ); } } } // 若该文件不存在,则输出信息 File Not Found }else{ printWriter.println("HTTP/1.1 404 File Not Found"); printWriter.println("Content-Type:text/html;charset=UTF-8"); printWriter.println("Content-Length:23"); printWriter.println(); printWriter.write("

    File Not Found

    "
    ); } printWriter.flush();//不返回任何值,只用于刷新流 } catch (Exception e) { e.printStackTrace(); }finally{ if(printWriter!=null){ printWriter.close(); } if(output!=null){ output.close(); } if(fis!=null){ fis.close(); } } } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    3项目的结构设计

    在这里插入图片描述

    4 效果展示

    在这里插入图片描述

  • 相关阅读:
    Instagram Shop如何开通?如何销售?最全面攻略
    Selenium Python 教程
    VUE3基础知识梳理
    投资风险管理
    第五章 :Spring Boot配置指南(二)
    【RTOS学习】精简RTOS源码 | 认识RTOS | 任务的创建和删除
    Java基础教程:多线程(4)-----线程的生命周期
    Dubbo服务提供者如何优雅升级?
    二叉树的遍历与构建问题
    Debian11安装Geoserver+矢量插件Vector Tiles
  • 原文地址:https://blog.csdn.net/IT_WEH_coder/article/details/126054172