http
超文本傳輸協(xié)議(HTTP,HyperText Transfer Protocol)是互聯(lián)網(wǎng)上應(yīng)用最為廣泛的一種網(wǎng)絡(luò)協(xié)議。所有的WWW文件都必須遵守這個(gè)標(biāo)準(zhǔn)。設(shè)計(jì)HTTP最初的目的是為了提供一種發(fā)布和接收HTML頁(yè)面的方法。1960年美國(guó)人Ted Nelson構(gòu)思了一種通過(guò)計(jì)算機(jī)處理文本信息的方法,并稱之為超文本(hypertext),這成為了HTTP超文本傳輸協(xié)議標(biāo)準(zhǔn)架構(gòu)的發(fā)展根基。Ted Nelson組織協(xié)調(diào)萬(wàn)維網(wǎng)協(xié)會(huì)(World Wide Web Consortium)和互聯(lián)網(wǎng)工程工作小組(Internet Engineering Task Force )共同合作研究,最終發(fā)布了一系列的RFC,其中著名的RFC 2616定義了HTTP 1.1。
java
Java是一門面向?qū)ο?a target="_blank">編程語(yǔ)言,不僅吸收了C++語(yǔ)言的各種優(yōu)點(diǎn),還摒棄了C++里難以理解的多繼承、指針等概念,因此Java語(yǔ)言具有功能強(qiáng)大和簡(jiǎn)單易用兩個(gè)特征。Java語(yǔ)言作為靜態(tài)面向?qū)ο缶幊陶Z(yǔ)言的代表,極好地實(shí)現(xiàn)了面向?qū)ο罄碚摚试S程序員以優(yōu)雅的思維方式進(jìn)行復(fù)雜的編程。
Java具有簡(jiǎn)單性、面向?qū)ο蟆⒎植际健⒔研浴踩浴⑵?**立與可移植性、多線程、動(dòng)態(tài)性等特點(diǎn)。Java可以編寫桌面應(yīng)用程序、Web應(yīng)用程序、分布式系統(tǒng)和嵌入式系統(tǒng)應(yīng)用程序等。
JAVA實(shí)現(xiàn)簡(jiǎn)易HTTP服務(wù)器
搭建一個(gè)服務(wù)器,要能接收請(qǐng)求,并給瀏覽器返回正確響應(yīng)。
項(xiàng)目的下載地址
項(xiàng)目目標(biāo):實(shí)現(xiàn)一個(gè)簡(jiǎn)易的多線程服務(wù)器,可以處理來(lái)自瀏覽器的請(qǐng)求(GET/POST),并做出正確的回應(yīng)。
請(qǐng)求分以下四種類型:
1. 無(wú)參數(shù),文本文件類型
2. 無(wú)參數(shù),圖片文件類型
3. 有參數(shù),GET方式請(qǐng)求,并完成表單驗(yàn)證(登陸驗(yàn)證)
4. 有參數(shù),POST方式請(qǐng)求,并完成表單驗(yàn)證(登陸驗(yàn)證)
首先,應(yīng)該明確這個(gè)項(xiàng)目的基本實(shí)現(xiàn)原理,從瀏覽器讀入用戶請(qǐng)求的信息,服務(wù)器解析并記錄返回的文件名和參數(shù)列表,如果文件存在,用流讀取文件,并返回到瀏覽器上,如果不存在,返回相應(yīng)的提示信息,參數(shù)列表和服務(wù)器存儲(chǔ)的相同的話,返回登陸成功,否則返回失敗。
第一步,既然要解析從瀏覽器傳過(guò)來(lái)的信息,那就要明白傳過(guò)來(lái)信息的所使用的協(xié)議HTTP\UDP\FTP 和 URL的組成元素,因?yàn)槭呛?jiǎn)易服務(wù)器,我們就只解析HTTP協(xié)議先。
Request是指從客戶端到服務(wù)器端的請(qǐng)求消息
Request 消息分為3部分,第一部分叫請(qǐng)求行, 第二部分叫http header, 第三部分是body. header和body之間有個(gè)空行,結(jié)構(gòu)如下圖
Method表示請(qǐng)求方法,比如”POST”,”GET”,
Path-to-resoure表示請(qǐng)求的資源,
Http/version-number 表示HTTP協(xié)議的版本號(hào),
當(dāng)使用的是”GET” 方法的時(shí)候, body是為空的,當(dāng)使用”POST”,body不為空,但是沒(méi)有換行,readline()方法不能讀
Response是指服務(wù)器端到客戶端的響應(yīng)信息
和Request消息的結(jié)構(gòu)基本一樣。 同樣也分為三部分,第一部分叫request line, 第二部分叫request header,第三部分是body. header和body之間也有個(gè)空行, 結(jié)構(gòu)如下圖
狀態(tài)碼用來(lái)告訴HTTP客戶端,HTTP服務(wù)器是否產(chǎn)生了預(yù)期的Response. HTTP/1.1中定義了5類狀態(tài)碼,1XX 提示信息 - 表示請(qǐng)求已被成功接收,繼續(xù)處理;2XX 成功 - 表示請(qǐng)求已被成功接收,理解,接受;3XX 重定向 - 要完成請(qǐng)求必須進(jìn)行更進(jìn)一步的處理;4XX 客戶端錯(cuò)誤 - 請(qǐng)求有語(yǔ)法錯(cuò)誤或請(qǐng)求無(wú)法實(shí)現(xiàn);5XX 服務(wù)器端錯(cuò)誤 - 服務(wù)器未能實(shí)現(xiàn)合法的請(qǐng)求,當(dāng)然不寫也是可以得,狀態(tài)碼就是便于程序員去分析當(dāng)前頁(yè)面是正確響應(yīng)還是錯(cuò)誤的。
第二步,在了解URL和HTTP協(xié)議之后,就可以開(kāi)始構(gòu)建項(xiàng)目了。
目前這個(gè)項(xiàng)目的UML圖
第三步,準(zhǔn)備文本、圖片、HTML文件,然后開(kāi)始編編編
效果圖:(端口號(hào):23333)2333…
默認(rèn)訪問(wèn)
aaron.txt
a.jpg
GET/POST請(qǐng)求
注意地址欄的變化
login.html(GET)
login.html(GET) (登陸失敗情況)
login.html(GET) (登陸成功情況)
login.html(POST)
login.html(POST) (登陸失敗情況)
login.html(POST) (登陸成功情況)
部分源碼:(全部源碼去上面下載)
//Server.java
package cn.net.sight.server;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
import cn.net.sight.thread.ServerThread;
public class Server {
private static ServerSocket server;
private static Properties properties;
private int port;
static {
properties = new Properties();
try {
properties.load(new FileInputStream(new File(“src/resources/property.proterties”)));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//initialize the server
public void init() {
try {
port = Integer.parseInt(properties.getProperty(“port”));
server = new ServerSocket(port);
} catch (IOException e) {
e.printStackTrace();
}
}
//receive the request from the web browser
public void receive() {
Socket clientSocket = new Socket();
try {
clientSocket = server.accept();
} catch (IOException e) {
e.printStackTrace();
}
ServerThread thread = new ServerThread(clientSocket);
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//startup server
public static void main(String[] args) {
Server Aaroncat = new Server();
Aaroncat.init();
System.out.println(“----Aaroncat has startup----”);
while(true){
Aaroncat.receive();
}
}
}
//Request.java
package cn.net.sight.server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import cn.net.sight.util.MessageUtil;
public class Request {
private InputStream input; // Socket --》 InputStream
private Socket socket; // client --》 socket
private BufferedReader buffer; // InputStream --》 BufferedReader
private String schema; // the schema of the request GET or POST
private String requestFileName; // exact file name
private String requestData; // file name + 《key=value》。。。
private String values_Str; // a string the user input in the form
private int paramLength; // using in the POST: the length of parameters
private Map《String, String》 socketValues;// values_str --》 MAP
private PrintStream print;
protected MessageUtil messageUtil = new MessageUtil();
//省略了全部的setter() getter()
private void doSchema(String firstLineInData) throws IOException {
socketValues = new HashMap《String, String》();
if (this.schema.equals(“GET”)) {
// GET請(qǐng)求 --》 包含文件名和參數(shù)鍵值對(duì)
// 實(shí)現(xiàn)了對(duì)FileName、SocketValues的賦值
this.setRequestData(messageUtil.getRequestData(firstLineInData));
if (this.requestData.contains(“?”)) {
this.setRequestFileName(messageUtil.getFileName(this.getRequestData()));
this.setSocketValues(messageUtil.getValues(this.getRequestData()));
} else {
// GET請(qǐng)求 --》只包含文件名
// 實(shí)現(xiàn)了對(duì)FileName的賦值
this.setRequestFileName(requestData);
}
} else {
// POST請(qǐng)求 第一行只包含文件名
// 實(shí)現(xiàn)了對(duì)FileName、SocketValues的賦值
this.setRequestFileName(messageUtil.getRequestData(firstLineInData));
this.getUserInfo(buffer);
}
}
private void getUserInfo(BufferedReader br) throws IOException {
while (this.buffer.ready()) {
String remained = buffer.readLine();
if (remained.contains(“Content-Length”)) {
String[] temp = remained.split(“ ”);
this.setParamLength(Integer.parseInt(temp[1]));
break;
}
}
buffer.readLine();
String userInfo = “”;
for (int i = 0; i 《 this.getParamLength(); i++) {
userInfo += (char) buffer.read();
}
this.setValues_Str(userInfo);
this.setSocketValues(messageUtil.getValues(this.getValues_Str()));
}
public Request(Socket clientSocket) throws IOException {
this.setSocket(clientSocket);
this.setPrint(new PrintStream(clientSocket.getOutputStream()));
this.setInput(clientSocket.getInputStream());
this.setBuffer(new BufferedReader(new InputStreamReader(clientSocket.getInputStream())));
// get something from the first line
String firstLineInData = buffer.readLine();
this.setSchema(messageUtil.getSchema(firstLineInData)); // 獲得請(qǐng)求方式Schema
doSchema(firstLineInData); // 對(duì)Schema進(jìn)行判斷
}
}
//Response.java
package cn.net.sight.server;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Date;
import java.util.Map;
import cn.net.sight.util.FileUtil;
import cn.net.sight.util.LoginUtil;
public class Response {
private String fileName;
private Map《String, String》 userValues;
private PrintStream ps;
private Request request;
private Socket clientSocket;
protected FileUtil fileUtil = new FileUtil();
protected LoginUtil loginUtil = new LoginUtil();
public String getFileName() {
return fileName;
}
//省略了全部的setter() 和 getter()
public Response(Request request) {
this.setRequest(request);
this.setClientSocket(request.getSocket());
this.setFileName(request.getRequestFileName());
userValues = this.request.getSocketValues();
try {
this.init();
} catch (IOException e) {
e.printStackTrace();
}
}
public void showup(String fileName) throws IOException {
this.ps = this.request.getPrint();
// 要處理正常文件名和空文件名
if (!fileName.equals(“”) && !fileName.equals(“error.html”)) {
this.ps.println(“HTTP/1.1 200 OK”);
this.ps.println();
fileUtil.readFile(fileName, ps);
} else if (fileName.equals(“error.html”)) {
ps.println(“HTTP/1.1 404 fileNotFound”);
ps.println();
fileUtil.readFile(“error.html”, ps);
} else {
ps.println(new Date().toString());
}
if (ps != null){
ps.close();
}
}
public void init() throws IOException {
//如果信息MAP是空,則代表是普通文件或者是默認(rèn)訪問(wèn)
if (userValues.isEmpty()) {
if (fileName != “”) {
this.showup(fileName);
} else {
this.ps = this.request.getPrint();
ps.println(new Date().toString());
if(ps != null) ps.close();
if(clientSocket != null)clientSocket.close();
}
} else {
//如果信息MAP不為空,代表是GET/POST請(qǐng)求,并帶有參數(shù)鍵值對(duì)
this.Check(userValues, fileName);
}
}
public void Check(Map《String, String》 values_list, String respFileName) throws IOException {
// 驗(yàn)證用戶輸入信息的合法性
if (loginUtil.isValid(values_list)) {
this.showup(respFileName);
} else {
this.showup(“error.html”);
}
}
}
//ServerThread.java
package cn.net.sight.thread;
import java.io.IOException;
import java.net.Socket;
import cn.net.sight.server.Response;
import cn.net.sight.server.Request;
public class ServerThread extends Thread {
private Socket clientSocket;
private Request request;
private Response response;
public ServerThread() {
super();
}
public ServerThread(Socket clientSocket) {
super();
this.clientSocket = clientSocket;
}
public Socket getClientSocket() {
return clientSocket;
}
public void setClientSocket(Socket clientSocket) {
this.clientSocket = clientSocket;
}
public Request getRequest() {
return request;
}
public void setRequest(Request request) {
this.request = request;
}
public Response getResponse() {
return response;
}
public void setResponse(Response response) {
this.response = response;
}
public void run(){
super.run();
try {
this.setRequest(new Request(clientSocket));
this.response = new Response(request);
this.setResponse(response);
} catch (IOException e) {
e.printStackTrace();
}
}
}
//FileUtil.java
package cn.net.sight.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
public class FileUtil {
private static final int BUFFER_SIZE = 1024;
public void readFile(String file_Name, PrintStream ps) throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
int length;
File file = new File(“src/resources/” + file_Name);
FileInputStream fis = null;
if (file.exists()) {
try {
fis = new FileInputStream(file);
while ((length = fis.read(buffer)) != -1) {
ps.write(buffer, 0, length);
ps.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} else {
ps.println(“File not found”);
ps.println();
}
}
}
//LoginUtil.java
package cn.net.sight.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
public class LoginUtil {
protected boolean flag = false;
protected Map《String, String》 values;
private static Properties properties;
static {
properties = new Properties();
try {
properties.load(new FileInputStream(new File(“src/resources/property.proterties”)));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public Map《String, String》 getValues() {
return values;
}
public void setValues(Map《String, String》 values) {
this.values = values;
}
// 驗(yàn)證用戶信息的合法性(應(yīng)用JDBC橋,連接數(shù)據(jù)庫(kù))
public boolean isValid(Map《String, String》 values) {
String username = properties.getProperty(“username”);
String password = properties.getProperty(“password”);
if (values.get(“username”).equals(username)) {
if (values.get(“password”).equals(password)) {
flag = true;
System.out.println(“The user ” + values.get(“username”) + “ was log the server.”);
return flag;
}
} else {
System.out.println(“Forbide the ” + values.get(“username”) + “ log the server”);
return flag;
}
return false;
}
}
//MessageUtil.java
package cn.net.sight.util;
import java.util.HashMap;
import java.util.Map;
public class MessageUtil {
// schema : GET or POST
public String getSchema(String requestMsg) {
String[] result = new String[1];
if (requestMsg.contains(“ ”)) {
result = requestMsg.split(“ ”);
requestMsg = result[0];
}
return requestMsg;
}
// get the resquestData = (filename + map《S,S》)
public String getRequestData(String firstLineInData) {
String[] result = new String[10];
result = firstLineInData.split(“ ”);
firstLineInData = result[1].substring(1);
return firstLineInData;
}
// get the filename from the requestData
public String getFileName(String requestData) {
String[] result = new String[10];
result = requestData.split(“[?]”);
return result[0];
}
// save the info into the map《S,S》
public Map《String, String》 getValues(String requestData) {
Map《String, String》 values = new HashMap《String, String》();
String[] result = new String[10];
String regex = “[&=]”;
if (requestData.contains(“?”)) {
result = requestData.split(“[?]”);
String data_List = result[1];
result = data_List.split(regex);
for (int i = 0; i 《 result.length - 1; i += 2) {
values.put(result[i], result[i + 1]);
}
return values;
} else {
result = requestData.split(regex);
for (int i = 0; i 《 result.length - 1; i += 2) {
values.put(result[i], result[i + 1]);
}
return values;
}
}
}
整個(gè)項(xiàng)目結(jié)構(gòu)
評(píng)論
查看更多