一、項(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)功能:
- 用戶能自由設(shè)置WebServer端口(1024-65535);
- 允許多個(gè)用戶同時(shí)訪問服務(wù)器;
- 實(shí)現(xiàn)HTML和JPG圖片的解析;
- 資源文件實(shí)現(xiàn)相對路徑解析;
二、整體思路
實(shí)現(xiàn)WebServer的邏輯是很清晰的:
- 設(shè)置WebServer端口號,其范圍在1024-65535之間;
- 使用 ServerSocket.accept()方法,輪詢監(jiān)聽用戶請求;
- 用戶使用瀏覽器輸入地址,向WebServer發(fā)出請求;
- 服務(wù)器監(jiān)聽到用戶請求,為該請求新建一個(gè)HttpServer來處理該請求;
- HttpServer解析用戶請求并作出響應(yīng);
- 用戶瀏覽器顯示響應(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中修改即可)。

resource文件夾下文件示例:

四、項(xiàng)目測試
將項(xiàng)目導(dǎo)出jar包;
-
在windows 系統(tǒng)cmd下,輸入java -jar WebServer.jar運(yùn)行WebServer.jar,根據(jù)提示,輸入合適的端口號,運(yùn)行webServer;
5.jpg -
用戶使用瀏覽器進(jìn)行請求(這里使用的請求鏈接是 http://localhost:1025/index.html 1025 為我們輸入設(shè)置的端口號);
4.jpg -
與此同時(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)載,違者必究!


