手寫一個最迷你的Web伺服器

船頭尺發表於2021-09-09

相信大家對Tomcat伺服器應該都很熟悉了,我們幾乎每天都會用它,它的安裝目錄結構大致如下:

圖片描述

今天我們就仿照Tomcat伺服器來手寫一個最簡單最迷你版的web伺服器,僅供學習交流。

1. 在你windows系統盤的F盤下,建立一個資料夾webroot,用來存放前端程式碼。

2. 建立一個簡單的Java專案,專案結構如下圖:

圖片描述

3. 程式碼介紹:

(1)ServerThread.java 核心程式碼,主要用於web檔案的讀取與解析等。程式碼如下:

package server;

import java.io.*;
import java.net.Socket;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName: ServerThread
 * @Description:
 * @Author: liuhefei
 * @Date: 2019/6/23
 * @blog: 
 **/
public class ServerThread implements Runnable {

    private static Map<String, String> contentMap = new HashMap<>();

    //可以參照Tomcat的web.xml配置檔案
    static {
        contentMap.put("html", "text/html");
        contentMap.put("htm", "text/html");
        contentMap.put("jpg", "image/jpeg");
        contentMap.put("jpeg", "image/jpeg");
        contentMap.put("gif", "image/gif");
        contentMap.put("js", "application/javascript");
        contentMap.put("css", "text/css");
        contentMap.put("json", "application/json");
        contentMap.put("mp3", "audio/mpeg");
        contentMap.put("mp4", "video/mp4");
    }

    private Socket client;
    private InputStream in;
    private OutputStream out;
    private PrintWriter pw;
    private BufferedReader br;

    private static final String webroot = "F:\webroot\";    //此處目錄,你可以自行修改

    public ServerThread(Socket client){
        this.client = client;
        init();
    }

    private void init(){
        //獲取輸入輸出流
        try {
            in = client.getInputStream();
            out = client.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void run() {
        try {
            gorun();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void gorun() throws Exception {
        //讀取請求內容
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        String line = reader.readLine().split(" ")[1].replace("/", "\");  //請求的資源
        if(line.equals("\")){
            line += "index.html";
        }
        System.out.println(line);
        String strType = line.substring(line.lastIndexOf(".")+1, line.length());  //獲取檔案的字尾名
        System.out.println("strType = " + strType);

        //給使用者響應
        PrintWriter pw = new PrintWriter(out);
        InputStream input = new FileInputStream(webroot + line);

        //BufferedReader buffer = new BufferedReader(new InputStreamReader(input));
        pw.println("HTTP/1.1 200 ok");
        pw.println("Content-Type: "+ contentMap.get(strType)  +";charset=utf-8");
        pw.println("Content-Length: " + input.available());
        pw.println("Server: hello");
        pw.println("Date: " + new Date());
        pw.println();
        pw.flush();

        byte[] bytes = new byte[1024];
        int len = 0;
        while ((len = input.read(bytes)) != -1){
            out.write(bytes, 0, len);
        }
        pw.flush();

        input.close();
        pw.close();
        reader.close();
        out.close();

        client.close();
    }
}

(2)HttpServer.java   (普通版)服務端

package server;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;

/**
 * @ClassName: HttpServer
 * @Description:  服務端
 * @Author: liuhefei
 * @Date: 2019/6/23
 * @blog: 
 **/
public class HttpServer {
    public static void main(String[] args) throws IOException {
        //啟動伺服器,監聽9005埠
        ServerSocket server = new ServerSocket(9005);
        System.out.println("伺服器啟動,監聽9005埠....");
        while (!Thread.interrupted()){
            //不停接收客戶端請求
            Socket client = server.accept();
            //開啟執行緒
            new Thread(new ServerThread(client)).start();
        }
        server.close();
    }
}

(2)HttpServer1.java  (執行緒池版)服務端

package server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @ClassName: HttpServer
 * @Description:  服務端
 * @Author: liuhefei
 * @Date: 2019/6/23
 * @blog: 
 **/
public class HttpServer1 {
    public static void main(String[] args) throws IOException {
        //建立執行緒池
        ExecutorService pool = Executors.newCachedThreadPool();

        //啟動伺服器,監聽9005埠
        ServerSocket server = new ServerSocket(9005);
        System.out.println("伺服器啟動,監聽9005埠....");
        while (!Thread.interrupted()){
            //不停接收客戶端請求
            Socket client = server.accept();
            //向執行緒池中提交任務
            pool.execute(new ServerThread(client));
        }
        server.close();
        pool.shutdown();
    }
}

4. 將一個具有index.html的靜態頁面檔案拷入到我們建立的webroot目錄下。相關的靜態web資原始碼可以到原始碼之家下載或是自己編寫。

5. 啟動web服務,啟動HttpServer.java 或HttpServer1.java都可以,服務啟動之後將會監聽9005埠。

6. 我們到瀏覽器上訪問我們的服務,訪問地址:

即可看到效果,我的效果如下:

圖片描述

控制檯列印如下:

圖片描述

到這裡,一個簡單的Web伺服器就完成了。

程式碼見:


僅供參考學習,感謝諸君的支援!


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3016/viewspace-2823243/,如需轉載,請註明出處,否則將追究法律責任。

相關文章