WebServer(三)
Readme:
~該版本改動:
- 將客戶端發送過來的請求完全解析出來,生成一個HttpRequest對象保存。便於以後操作
- 步驟:
·新建一個包:http
·在哪http包中定義類HttpRequest,用其每一個實例表示一個具體的客戶端發送過來的請求。
·在ClientHandler中實例化HttpRequest並將Socket傳入,以便HttpRequest獲取輸入流解析客戶端請求
·在HttpRequest的構造方法中完成解析工作:由於一個請求含有三部分:請求行、消息頭、消息正文,所以在構造方法中也分爲三部分進行解析。
Webserver:
HttpRequest
package com.senbao.webserver.http;
/*
* HttpRequest表示一個Http協議要求的請求信息
* 一個請求包含三部分:請求行 消息頭 消息正文
*/
public class HttpRequest {
//對應客戶端的socket
private Socket socket;
//通過socket獲取到的輸入流,用於讀取客戶端發送的請求
private InputStream in;
/*
* 請求行相關信息定義
*/
//請求方式
private String method;
//資源路徑
private String url;
//請求使用的協議版本
private String protocol;
/*
* 消息頭相關信息
*/
private Map<String,String> handlers = new HashMap<>();
/**
* 實例化HttpRequest使用的構造方法,需要將對應客戶端的socket傳入,
* 以便讀取客戶端發送過來的請求內容
* @param socket
*/
public HttpRequest(Socket socket) {
System.out.println("HttpRequest:開始解析請求");
try {
this.socket = socket;
this.in = socket.getInputStream();
/*
* 1.解析請求行
* 2.解析消息頭
* 3.解析消息正文
*/
//1
parseRequestLine();
//2
parseHeaders();
//3
parseContent();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 解析請求行
*/
private void parseRequestLine() {
System.out.println("解析請求行");
/*
大致流程:
1.通過輸入流讀取第一行字符串
2.將請求行按照空格拆分成三項
3.將拆分的三項分別放在method、url、protocol三個屬性上
解析請求行時,在獲取拆分後的數組元素時可能會引發數組下標越界,這是由於HTTP協議允許客戶端發送一個空請求過來導致的,我們後面解決。
*/
String line = readLine();
String[] array = line.split("\\s");
method = array[0];
url = array[1];
protocol = array[2];
System.out.println("method:"+method);
System.out.println("url:"+url);
System.out.println("protocal:"+protocol);
System.out.println("解析完畢");
}
/**
* 解析消息頭
*/
private void parseHeaders(){
System.out.println("解析消息頭");
/*
* 大致步驟
* 1.繼續使用readLine方法讀取若干行內容,每一行都是一個消息頭
* 2.當readLine方法返回值爲空字符串時,停止循環(單獨讀到CRLF時,readLine返回空字符串)
* 3.每當讀取一個消息頭信息時應當按照“:”拆分爲兩項,第一項作爲消息頭名字,第二項作爲
* 消息頭對應的值,將名字作爲key,值作爲value存入到handlers這個map中
*/
while(true) {
String line = readLine();
//判斷是否單獨讀取到了CRLF
if("".equals(line)) {
break;
}
String[] data = line.split(":\\s");
handlers.put(data[0], data[1]);
}
System.out.println("handler:" + handlers);
System.out.println("解析完畢");
}
/**
* 解析消息正文
*/
private void parseContent() {
System.out.println("解析消息正文");
System.out.println("解析完畢");
}
/**
* 通過給定的輸入流讀取一行字符串(以CRLF結尾)
* @param in
* @return
*/
private String readLine() {
try {
StringBuilder bulder = new StringBuilder();
int d = -1;
char c1 = 'a',c2 = 'a';
while((d = in.read())!=-1) {
c2 = (char)d;
/*
* 在ASCII碼中CR的編碼對應的數字爲13, LF對應的編碼爲10
* 就好比字符a對應的編碼爲97
*/
if(c1 == 13 && c2 == 10) {
break;
}
bulder.append(c2);
c1 = c2;
}
return bulder.toString().trim();
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
public String getMethod() {
return method;
}
public String getUrl() {
return url;
}
public String getProtocol() {
return protocol;
}
public String getHeader(String name) {
return handlers.get(name);
}
}
ClientHandler
package com.senbao.webserver.core;
public class ClientHandler implements Runnable{
private Socket socket;
public ClientHandler(Socket socket) {
this.socket = socket;
}
public void run() {
/*
* 處理該客戶端的請求的大致步驟
* 1.解析請求
* 2.處理請求
* 3.給予響應
*/
try {
//1.解析請求,生成HttpRequest對象
HttpRequest request = new HttpRequest(socket);
} catch (Exception e) {
e.printStackTrace();
}
}
}
WebServer
package com.senbao.webserver.core;
public class WebServer {
private ServerSocket server;
public WebServer(){
try {
//Tomcat默認開啓的端口就是8080
server = new ServerSocket(8080);
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() {
try {
// while(true) {
System.out.println("等待客戶端連接……");
Socket socket = server.accept();
System.out.println("一個客戶端連接了!");
//啓動一個線程,處理客戶端請求
ClientHandler handler = new ClientHandler(socket);
Thread t = new Thread(handler);
t.start();
// }
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
WebServer server = new WebServer();
server.start();
}
}