day15-WebServer(三)

                                            WebServer(三)

Readme:

~該版本改動:

  1. 將客戶端發送過來的請求完全解析出來,生成一個HttpRequest對象保存。便於以後操作
  2. 步驟:
    ·新建一個包: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();
		}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章