• Socket——实现一个简单的静态网页服务器


    整体结构就是使用ServerSocket监听一个地址,当有接受到请求之后,解析请求中的资源路径。服务器资源路径存放在项目下的一个目录中,服务器会到这个目录中根据请求的路径去寻找相应的资源。如果找到了则返回该文件内容,否则提示找不到文件。

    功能主要分为三块,一块是监听IP和端口号;一块是接受HTTP请求报文,并解析报文;最后是处理和返回响应。

    HttpServer.java

    package com.oolong.webserver;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class HttpServer {
    
        public static final String WEB_ROOT =
                System.getProperty("user.dir") + File.separator  + "webroot";
        private final int port = 8080;
        private boolean isShutdown = false;    // 表示服务器是否关闭
        
        public void waiting() {
            ServerSocket serverSocket = null;
            
            try {
                serverSocket = new ServerSocket(port, 1, 
                        InetAddress.getByName("127.0.0.1"));
            } catch (IOException ex) {
                ex.printStackTrace();
                System.exit(1);
            }
            
            // 循环等待请求
            while(!isShutdown) {
                Socket socket = null;
                InputStream input = null;
                OutputStream output = null;
                
                try {
                    // 从服务器等待队列中获取一个连接
                    socket = serverSocket.accept();
                    input = socket.getInputStream();
                    output = socket.getOutputStream();
                    
                    // 从输入中解析请求字符串,生成一个请求对象
                    HttpRequest request = new HttpRequest(input);
                    request.parse();
                    
                    // 创建一个响应对象返回内容
                    HttpResponse response = new HttpResponse(output);
                    response.setRequest(request);
                    response.sendStaticResource();
                    
                    // 关闭socket
                    socket.close();
                    
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
        
        public static void main(String[] args) {
            HttpServer server = new HttpServer();
            server.waiting();
        }
    }

    这里利用ServerSocket接受请求的Socket:

    socket = serverSocket.accept();

    然后从请求的Socket中获取到输入流和输出流:

    input = socket.getInputStream();
    output = socket.getOutputStream();

    将输入流交给一个HttpRequest对象处理,进行解析请求:

    HttpRequest request = new HttpRequest(input);
    request.parse();

    将解析后的请求对象和输出流交给HttpResponse对象,用来返回响应:

    HttpResponse response = new HttpResponse(output);
    response.setRequest(request);
    response.sendStaticResource();

    下面分别看看请求对象和响应对象的实现。

    HttpRequest.java

    package com.oolong.webserver;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    public class HttpRequest {
    
        private InputStream input;
        private String uri;
        
        public HttpRequest(InputStream input) {
            this.input = input;
        }
        
        public void parse() {
            // 从socket中读取字符流
            StringBuffer requestStr = new StringBuffer(2048);
            int i;
            byte[] buffer = new byte[2048];
            
            try {
                i = input.read(buffer);
            } catch (IOException ex) {
                ex.printStackTrace();
                i = -1;
            }
            
            for (int j = 0; j < i; j++) {
                requestStr.append((char) buffer[j]);
            }
            
            System.out.println(requestStr.toString());
            uri = parseUri(requestStr.toString());
            System.out.println(uri);
        }
        
        private String parseUri(String requestStr) {
            int index1, index2;
            index1 = requestStr.indexOf(' ');
            
            if (index1 != -1) {
                index2 = requestStr.indexOf(' ', index1 + 1);
                
                if (index2 > index1) {
                    return requestStr.substring(index1 + 1, index2);
                }
            }
            
            return null;
        }
        
        public String getUri() {
            return uri;
        }
    }

    可以看到这个请求对象中主要就是parse()这个解析请求字符的方法,以及parseUri()这个解析URI的方法。

    首先看parse(),它创建了一个缓冲区,然后从输入流中读取请求字符串。然后调用parseUri()解析请求路径。

    HttpResponse.java

    这个是处理响应的类,看起来稍显复杂。其实只是根据请求解析的URI到资源目录中去寻找对应的文件,然后将文件写入Socket的输出流中。

    如果没有找到,则输出一段错误信息即可。

    package com.oolong.webserver;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.OutputStream;
    
    public class HttpResponse {
    
        private static final int BUFFER_SIZE = 1024;
        private HttpRequest request;
        private OutputStream output;
        
        public HttpResponse(OutputStream output) {
            this.output = output;
        }
        
        public void setRequest(HttpRequest request) {
            this.request = request;
        }
        
        public void sendStaticResource() throws IOException {
            byte[] bytes = new byte[BUFFER_SIZE];
            FileInputStream fis = null;
            String filePath = request.getUri() == null ? "" : request.getUri().trim();
            
            // 处理根路径
            if (filePath.equals("/")) {
                filePath = "/index.html";
            }
            
            try {
                String page = null;
                File file = new File(HttpServer.WEB_ROOT, filePath);
                Long fileLength = file.length();
                byte[] fileContent = new byte[fileLength.intValue()];
                
                if (file.exists()) {
                    fis = new FileInputStream(file);
                    fis.read(fileContent);
                    fis.close();
                    
                    page = new String(fileContent);
                    page = warpMessage("200", page);
                    output.write(page.getBytes());
                        
                } else {
                    String errorMessage = warpMessage("404", "404 File Not Found!");
                    output.write(errorMessage.getBytes());
                }
            } catch(Exception ex) {
                ex.printStackTrace();
            }
        }
        
        private String warpMessage(String statusCode, String message) {
            return "HTTP/1.1 " + statusCode + "
    " +
                      "Content-Type: text/html
    " +
                      "Content-Length: " + message.length() + "
    " +
                      "
    " + message;
        }
        
        public static void main(String[] args) {
            System.out.println(HttpServer.WEB_ROOT);
        }
    }

    测试

    在项目的根目录下的webroot目录中创建一个简单的index.html页面。

    <!DOCTYPE html>
    <html>
    <head>
        <title></title>
    </head>
    <body>
        Hello World!
    </body>
    </html>

    运行HttpServer。然后在浏览器中输入http://127.0.0.1:8080/ 或者http://127.0.0.1:8080/index.html

    可以看到:

    如果输入其他地址,如:http://127.0.0.1:8080/home.html

    注意

    你可能在调试的时候会发现,在浏览器发起一次请求的时候,

    socket = serverSocket.accept();

    可能会执行两次,这是因为浏览器会自动发起一次对icon的请求。这个是浏览器的特性,与代码无关,不是bug。

  • 相关阅读:
    iOS
    iOS
    iOS
    iOS
    iOS
    iOS
    iOS
    iOS
    iOS
    【转】idea 用maven骨架生成项目速度慢的问题
  • 原文地址:https://www.cnblogs.com/weilu2/p/a_simple_static_html_webserver.html
Copyright © 2020-2023  润新知