Java使用Scoket實(shí)現(xiàn)一個(gè)簡單的Web服務(wù)器

一、項(xiàng)目介紹

該項(xiàng)目是用JAVA使用Scoket實(shí)現(xiàn)一個(gè)簡單的Web服務(wù)器,項(xiàng)目的實(shí)現(xiàn)受到了 HaHa_Sir《Java寫一個(gè)簡單的Web服務(wù)器Socket實(shí)現(xiàn)》 的啟發(fā)。

項(xiàng)目已實(shí)現(xiàn)功能:

  1. 用戶能自由設(shè)置WebServer端口(1024-65535);
  2. 允許多個(gè)用戶同時(shí)訪問服務(wù)器;
  3. 實(shí)現(xiàn)HTML和JPG圖片的解析;
  4. 資源文件實(shí)現(xiàn)相對路徑解析;

二、整體思路

實(shí)現(xiàn)WebServer的邏輯是很清晰的:

  1. 設(shè)置WebServer端口號,其范圍在1024-65535之間;
  2. 使用 ServerSocket.accept()方法,輪詢監(jiān)聽用戶請求;
  3. 用戶使用瀏覽器輸入地址,向WebServer發(fā)出請求;
  4. 服務(wù)器監(jiān)聽到用戶請求,為該請求新建一個(gè)HttpServer來處理該請求;
  5. HttpServer解析用戶請求并作出響應(yīng);
  6. 用戶瀏覽器顯示響應(yīng)結(jié)果;

三、代碼實(shí)現(xiàn)

該項(xiàng)目為WebServer,整個(gè)項(xiàng)目包含兩個(gè)java文件,分別是webserver.java 和 httpserver.java。

1. WebServer.java

其中,WebServer.java 是整個(gè)項(xiàng)目的主線程,用于設(shè)置服務(wù)器端口號;監(jiān)聽用戶的請求,為每一個(gè)監(jiān)聽到的請求新建一個(gè)httpserver線程,來處理用戶請求。

函數(shù) 介紹
public static void startServer(int port) 輪詢serverSocket.accept(),監(jiān)聽用戶請求,為每一個(gè)監(jiān)聽到的請求,新建一個(gè)httpserver線程響應(yīng)
public static void main(String[] args) 用于根據(jù)用戶輸入來設(shè)置服務(wù)器端口號,其后,啟動(dòng)Webserver服務(wù)器
package com.webServer;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;


public class WebServer {
    
    public static void startServer(int port){
        try {
            @SuppressWarnings("resource")
            ServerSocket serverSocket = new ServerSocket(port);
            while(true){
                Socket socket = serverSocket.accept();
                new HttpServer(socket).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    
    
    public static void main(String[] args){
        @SuppressWarnings("resource")
        Scanner in =new Scanner(System.in);
        System.out.print("Please input the Port Number( 1024 -65535 ):");
        int portNumber=80;
        do{
            portNumber=in.nextInt();
            if(portNumber>=1024 && portNumber <= 65535 )
                break;
            else
                System.out.print("The Input Port Number is wrong,please input again( 1024 -65535 ):");
        }while(in.hasNext());
        System.out.println("**********WebServer start!*********");
        startServer(portNumber);
    }
}

2. Httpserver.java

Httpserver.java 繼承自Thread,用于讀取用戶訪問路徑,根據(jù)路徑響應(yīng)請求。

函數(shù) 介紹
public HttpServer(Socket socket) 初始化socket對象,獲取對應(yīng) 輸入,輸出流
public void run() 重載Run()函數(shù),調(diào)用Read()、response()函數(shù)
private void response(String filePath) 根據(jù)讀取的路徑,進(jìn)行響應(yīng)。以流的形式讀取文件,再以流的形式輸出文件
private String read() 解析請求路徑
package com.webServer;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
 

public class HttpServer extends Thread {
    //web資源路徑,此處為相對路徑
    public static final String ROOT = "./resource";
    
    //輸入流對象,讀取瀏覽器請求
    private InputStream input;
    
    //輸出流對象,響應(yīng)內(nèi)容給瀏覽器
    private OutputStream out;
    
    //因?yàn)榫幋a問題,text/html,若直接使用out輸出會(huì)使用默認(rèn)編碼,而不是UTF-8,因此會(huì)導(dǎo)致中文亂碼
    private OutputStreamWriter osw;
 
    //初始化socket對象,獲取對應(yīng) 輸入,輸出流
    public HttpServer(Socket socket) {
        try {
            input = socket.getInputStream();
            out = socket.getOutputStream();
            osw = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    //多線程方法調(diào)用
    @Override
    public void run() {
        String filePath = read();
        response(filePath);
    }
 
    private void response(String filePath) {
        File file = new File(ROOT + filePath);
        //System.out.println("filePath:"+filePath);
        System.out.println("filePath():"+file.getPath());
        //System.out.println("file.getName():"+file.getName());
        
        //防止用戶直接輸入"localhost:portnumber",導(dǎo)致路徑為".\resource"而出現(xiàn)錯(cuò)誤。
        if (file.exists() && !file.getPath().equals(".\\resource")) {
            //資源存在,讀取資源
            try {
                //獲取文件類型,判斷Content-Type
                String fileType =file.getName().split("\\.")[1];
                //System.out.println("fileType:"+fileType);
                if(fileType.equals("html")) {
                    FileInputStream fis = new FileInputStream(file);   
                    InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
                    BufferedReader reader = new BufferedReader(isr);
                    StringBuffer sb = new StringBuffer();
                    String line = null;
                    while ((line = reader.readLine()) != null) {
                        sb.append(line).append("\r\n");
                    }
                    StringBuffer result = new StringBuffer();
                    result.append("HTTP/1.1 200 OK \r\n");
                    result.append("accept-ranges: bytes \r\n");
                    result.append("Content-Type:text/html;charset=UTF-8 \r\n");
                    result.append("Content-Length:" + file.length() + "\r\n");
                    result.append("\r\n" + sb.toString());
                    //System.out.println(result.toString());   
                    osw.write(result.toString());   
                    osw.flush();
                    osw.close();
                    fis.close();
                    isr.close();
                    reader.close();
                }else if(fileType.equals("jpg")) {
                    StringBuffer result = new StringBuffer();
                    result.append("HTTP/1.1 200 OK \r\n");
                    result.append("accept-ranges: bytes \r\n");
                    result.append("Content-Type:image/jpeg \r\n");
                    result.append("Content-Length:" + file.length() + "\r\n");
                    result.append("\r\n");
                    //System.out.println(result.toString());   
                    out.write(result.toString().getBytes());   
                    FileInputStream fis = new FileInputStream(file);
                        byte[] buf=new byte[1024];
                        int len=0; 
                    while((len=fis.read(buf))!=-1){
                       out.write(buf,0,len);
                    }
                    out.flush();
                    out.close();
                    fis.close();
                }
                
            } catch (Exception e) {
                e.printStackTrace();                                    
            }
 
        } else {   
            //資源不存在,提示 File not found
            try {
                StringBuffer error = new StringBuffer();
                String html="<h1>404 File Not Found.</h1>";
                
                error.append("HTTP/1.1 404 Not Found \r\n");
                error.append("Content-Type:text/html;charset=UTF-8 \r\n");
                error.append("Content-Length:").append(html.length()).append("\r\n").append("\r\n");
                error.append(html);

                osw.write(error.toString());
                osw.flush();
                osw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
 
    }
 
    //解析請求路徑
    private String read() {
        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        try {
            // 讀取請求頭, 如:GET /index.html HTTP/1.1
            String readLine = reader.readLine();
            // 防止用戶直接輸入"localhost:portnumber"而出現(xiàn)錯(cuò)誤。
            System.out.println(readLine);
            if(readLine==null)
                return "/";
            String[] split = readLine.split(" ");
            if (split.length != 3) {
                return "/";
            }
            return split[1];
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}

3.Index.html

用戶請求的頁面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebServerIndex</title>
</head>
<body>
<div style="margin-top: 5%; text-align: center;height: 100%;">
<!-- 該圖片路徑為resource下的相對路徑,若想放自己的圖片,則將其命名為picture.jpg即可 -->
    <img src="/picture.jpg" alt=""/>
    <h1 style="font-size: 72px">Hello World!你好,世界!</h1>
</div>
</body>
</html>

4.資源路徑

為了項(xiàng)目的可移植性,文件的解析路徑是相對路徑,整個(gè)文件都放在文件夾resource下(若想修改相對路徑,在HttpServer.java ROOT中修改即可)。


1.jpg

resource文件夾下文件示例:


2.jpg

四、項(xiàng)目測試

  1. 將項(xiàng)目導(dǎo)出jar包;

  2. 在windows 系統(tǒng)cmd下,輸入java -jar WebServer.jar運(yùn)行WebServer.jar,根據(jù)提示,輸入合適的端口號,運(yùn)行webServer;


    5.jpg
  3. 用戶使用瀏覽器進(jìn)行請求(這里使用的請求鏈接是 http://localhost:1025/index.html 1025 為我們輸入設(shè)置的端口號);

    4.jpg

  4. 與此同時(shí),cmd運(yùn)行的WebServer情況如下;


    3.jpg

備注:Index.html在WebServer.jar路徑下resource文件夾下

作者簡介:一木一生,一個(gè)散發(fā)著單身狗清香的程序猿。同時(shí),歡迎關(guān)注我的CSDN博客 Vito_w7

本文為作者原創(chuàng),未經(jīng)允許,不得轉(zhuǎn)載,違者必究!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 4,011評論 0 11
  • 轉(zhuǎn)自陳明乾的博客,可能有一定更新。 轉(zhuǎn)原文聲明: 原創(chuàng)作品,允許轉(zhuǎn)載,轉(zhuǎn)載時(shí)請務(wù)必以超鏈接形式標(biāo)明文章 原始出處 ...
    LUNJINGJIE閱讀 4,101評論 1 33
  • 逆向思維 什么是逆向思維呢?正向思維可能大家都很熟悉,比如說今天出門看到天上烏云密布,判斷可能要下雨,知道出門要帶...
    何慧閱讀 522評論 0 0
  • 8月的第二天,周日,夏日的炎熱讓人心煩意亂,真的僅僅是夏日的炎熱嗎? 對我來說,整個(gè)七月應(yīng)該是黑色,股市投資失利,...
    _Record_閱讀 198評論 0 1
  • 蝶戀花(詞林正韻) 寂寞庭深愁緒鬧,落葉情堪,秋雨寒風(fēng)嘯。佇望梧桐凋寂悄,殘燈獨(dú)晃知天曉。 欲盡此情痕淚照,難與傾...
    葉楚易閱讀 611評論 9 18

友情鏈接更多精彩內(nèi)容