網絡——深入瞭解Http

一、Http網絡請求的原理

Http是通過TCP實現的可靠的網絡傳輸。(需要了解的知識TCP/UDP編程)
那麼Http是如何過度到TCP實現客戶端與服務器的交互的呢?
①、當客戶端執行網絡請求的時候,會從URL地址中解析出URL的主機名(Host),並將主機地址轉換成IP
(主機名是什麼:類似http://write.blog.csdn.net/postedit 
    IP是什麼:類似192.168.1.1。主機名是IP的表示,因爲主機名錶達的含義肯定比數字容易記住
②、從URL解析出服務器的所用的端口號。
③、客戶端用TCP連接服務器
④、連接成功之後,獲取輸出流,並將數據以報文(報文的格式之後再講)的形式傳遞給服務器(Request)
⑤、當服務器接受到數據之後,進行判斷和解析嗎,並回送一條響應報文(Response)
⑥、客戶端從輸入流中獲取報文,並解析。
⑦、最後關閉網絡的連接。

二、Http請求服務器的方式。(詳細知識,需要了解報文的格式,之後才能做實驗)

①、GET請求
作用:獲取服務器的某個資源,或者說是告訴服務器,我想查詢一些信息。
特點:明文傳輸(直接將傳送給服務器的數據,寫在URL上)。
常用:跳轉到另一個網站,網站名就是GET傳輸,然後向服務器請求獲取該網站。

②、POST請求
作用:向服務器傳遞數據。一般用來提交HTML表單時使用。服務器處理這些數據
特點:表單上傳。(寫入的數據,不顯示在URL上)
常用:註冊用戶名、密碼。傳送給服務器處理。

其實,從作用上看,兩者其實沒有什麼區別,都是Client傳輸數據給Service。不過一個是寫在URL上,一個是寫在form表單上。然後Service端根據這些上傳的數據進行處理。

③、PUT請求
作用:向服務器寫入資源。在服務器創造一個文本,然後將Client端的數據,PUT到該文本中。(如果文本名重複的話,前一個文件的內容就會被覆蓋)

④、DELETE請求
作用:從服務器中刪除資源。(無法保證是否被刪除,HTTP規範允許不通知客戶端拒絕該請求)

⑤、HEAD請求
作用:讓Service端只返回報文的頭部(報文分爲三部分:狀態、頭部、數據)。Client端根據報文的頭部,能夠判斷(數據域的信息,判斷該報文是否存在數據等。原理需要講到報文的時候才能說清楚)

⑥、TRACE請求
作用:當Client端發送請求時候,可能需要經過防火牆、代理、網關等應用程序,這些程序可能會修改原始的HTTP請求,然後將最終的HTTP發送給Service端。當Service端接收之後,會回調將修改後的HTTP請求,回傳給Client端。

⑦、OPTION請求
作用:詢問服務器支持哪些方法(比如POST、GET、OPTION方法)


三、如何解析HTTP報文

①、請求報文的格式
<起始行>
<首部>
<空行>
<數據域>

示例:
GET http://my.csdn.net/my/mycsdn HTTP1.1
Content-Type:text/plain
Host:www.my.csdn.net
Content-Length:30

username="chen"&pwd = 123

②、回覆報文的格式
<狀態欄>
<首部>
<空行>
<數據>
從格式,來看其實就是狀態欄不太一樣。
Respone的狀態欄:版本號  狀態碼  對狀態碼的描述 
版本號就沒什麼好說的。
狀態碼是什麼:
100~199:表示請求以及接收到了
200~299:請求成功。Service已經處理
300~399:要求完成請求需要更進一步的動作
400~499:客戶端錯誤。表示請求語法錯誤,或者是請求無法執行
500~599:服務器端錯誤。服務器無法實現請求。

常用狀態碼:(狀態碼,後面的就是描述)
200 OK:客戶端請求成功
400 Bad Request:客戶端請求語法有錯。
403 Forbidden:服務器接受到請求,但是拒絕服務
404 Not Found:請求資源不存在。可能原因:URL錯誤
500 Internal Server Error:服務器發生不可預期錯誤
503 Server Unavailable:服務器當前無法處理客戶端請求

示例:
HTTP1.1 200 OK :表示成功接收

③、首部的屬性
首部的分類:①、請求的首部屬性  ②、響應的首部屬性  ③、通用的首部屬性  ④、自定義的首部屬性
介紹請求的首部屬性:
Content-Type:請求數據的類型(MIME)
Content-Length:請求數據的長度
Cache-Control:是否緩存
Host:請求數據的主機名
User-Agent:發出請求的瀏覽器
Accept:客戶端可識別的類型列表
Accept-Encoding:客戶端可識別的類型編碼
Connection:設置連接方式。KeepAlive表示保持連接
Transfer-Encoding:告訴接收端,爲保證可靠傳輸,對報文采取了什麼編碼

重要的屬性
Content-Type擁有的屬性:
超文本標記語言文本 .html                     text/html
xml文檔 .xml                                          text/xml
XHTML文檔 .xhtml                                application/xhtml+xml
普通文本 .txt                                          text/plain
RTF文本 .rtf                                           application/rtf
PDF文檔 .pdf                                         application/pdf
Microsoft Word文件 .word                     application/msword
PNG圖像 .png                                       image/png
GIF圖形 .gif                                           image/gif
JPEG圖形 .jpeg,.jpg                              image/jpeg
au聲音文件 .au                                      audio/basic
MIDI音樂文件 mid,.midi                         audio/midi,audio/x-midi
RealAudio音樂文件 .ra, .ram                 audio/x-pn-realaudio
MPEG文件 .mpg,.mpeg                        video/mpeg
AVI文件 .avi                                          video/x-msvideo
GZIP文件 .gz                                        application/x-gzip
TAR文件 .tar                                         application/x-tar
Json數據:.json                                   application/json
任意的二進制數據                                application/octet-stream


四、解析每個請求的HTTP報文

①、GET請求  
例:輸入http://write.blog.csdn.net/login.php?username=chen&pwd=123
直接將數據寫在URL就是明文傳輸
則創建的報文是:
GET login.php/username=chen&pwd=123 HTTP1.1
Host:http://write.blog.csdn.net

服務器返回的報文數據:
HTTP/1.1 200 OK
Content-Type:text/html
Content-Length:1024

<Html>
  網頁數據
</Html>

②、POST請求
例:從http://write.blog.csdn.net/login.php中,上傳HTML表單
POST login.php HTTP1.1
Accept-Encoding:gzip
Content-Type:multipart/form-data; boundary=ABCD           //Content-Type:表示這是一個表單  boundary:表示分隔符
Content-Length:22350
Host:http://write.blog.csdn.net
Connection:Keep-Alive

--ABCD                                                                  //通過--加上boundary的值:表示這是表單的一個數據
Content-Disposition:form-data;name="username"                  //key值
Content-Type:text/plain; charset=UTF-8                                    //key的類型
Content-Transfer-Encoding:8bit                                              //key的大小
                                                                                                  //空白行,必須加
chen                                                                                          //value的值
--ABCD
Content-Disposition:form-data;name="images";filename="storage/emulated/0/Camera/picture/tempeter.jpg"
Content-Type: application/octet-stream                      
Content-Transfer-Encoding:binary

(這裏是圖片的二進制數據)
--ABCD--    //表示終止



服務器返回的數據:
HTTP/1.1 200 OK
Content-Type:text/plain
Content-Length:21

success

③、PUT請求
PUT new-text.txt HTTP/1.1
Host:www.myhost.cn
Content-Type:text/html
Content-Length:21    

This  is file content           //文件的內容


服務器返回的數據:
HTTP/1.1 200 OK
Content-Type:text/html
Content-Length:1024
Location:www.myhost.cn/new-text.txt

www.myhost.cn/new-text.txt

④、HEAD請求
HEAD  login.php HTTP/1.1
Content-Type:text/plain
Content-Length:1024

服務器返回的數據
HTTP/1.1 200 OK
Content-Type:text/html
Content-Length:12324

⑤、TRANCE請求  假設經過了一層代理商(proxy.vpn.com)
TRANCE  login.php HTTP/1.1
Host:www.myhost.com

服務器返回的數據:
HTTP/1.1 200 OK
Content-Type:text/plain
Content-Length:21

TRANCE login.php HTTP/1.1
Host:www.myhost.com
Via:www.proxy.vpn.com


⑥、OPTION請求
OPTION login.php HTTP/1.1
Host:www.myhost.com

服務器返回的數據:
HTTP/1.1 200 OK
Allow:GET、POST、PUT、OPTIONS
Content-Length:0

五、利用TCP模擬HTTP傳輸

①、創建SimpleServer等待Socket連接。
public class SimpleServer extends Thread{
	public static final int PORT = 8000;
	private ServerSocket mServerSocket = null;
	public SimpleServer(){
		try {
			mServerSocket = new ServerSocket(PORT);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.out.println("端口"+PORT+"已經被佔用,請換一個端口");
		}
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		//死循環,接收客戶端的TCP
		while(true && mServerSocket != null){
			System.out.println("等待客戶端連接");
			//調用線程處理數據
		}
	}
}

②、TCP連接成功後,Server端處理數據(創建DeliverThread來處理數據)
public class DeliverThread extends Thread {

	private Socket mServerSocket;
	private BufferedReader mBufferedReader;
	private PrintWriter mPrintWriter;
	
	public DeliverThread(Socket socket){
		mServerSocket = socket;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		try {
			//獲取輸入輸出流
			mBufferedReader = new BufferedReader(new InputStreamReader(mServerSocket.getInputStream()));
			mPrintWriter = new PrintWriter(mServerSocket.getOutputStream());
			//處理Client發送過來的數據
			parseRequest();
			//回覆Client的數據
			handleResponse();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			try {
				mBufferedReader.close();
				mPrintWriter.close();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

		}
	}
	
	private void parseRequest() throws IOException{
		String line = null;
		int lineNum = 0;
		//首先解析頭部狀態數據
		//然後解析頭部數據
		//最後解析傳遞的數據
		while((line = mBufferedReader.readLine()) != null){
			//如果爲第一行,那麼一定是狀態欄
			if (lineNum == 0){
				parseHeaderLine(line);
			}
			lineNum++;
		}
		System.out.println("解析Service數據完成");
	}
	
	private void parseHeaderLine(String line){
		//分割Params
		String data[] = line.split(" ");
		System.out.println("請求類型"+data[0]);
		System.out.println("請求的網址"+data[1]);
		System.out.println("Http版本:"+data[2]);
	}
	
	private void handleResponse(){
		mPrintWriter.println("Http/1.1 200 OK");
		mPrintWriter.println("Content-Type:text/plain");
		mPrintWriter.println("Content-Length:300");
	}
}

③、創建Socket端。
ublic class HttpPost extends Thread {
	private String mUrl;
	private Map<String, String> mParams;
	private Socket mClientSocket;
	public HttpPost(String url){
		mUrl = url;
		mParams = new HashMap<>();
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		try {
			mClientSocket = new Socket(mUrl,SimpleServer.PORT);
			BufferedReader reader = new BufferedReader(new InputStreamReader(mClientSocket.getInputStream()));
			PrintWriter writer = new PrintWriter(mClientSocket.getOutputStream());
			writeHeader(writer);
			waitResponse(reader);
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				mClientSocket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	private void writeHeader(PrintWriter writer){
		writer.println("POST login.php HTTP/1.1");
	}
	
	private void waitResponse(BufferedReader reader){
		StringBuilder builder = new StringBuilder();
		String str = null;
		try {
			while((str = reader.readLine()) != null){
				builder.append(str);
			}
			System.out.println(builder.toString());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void addParams(String key,String value){
		mParams.put(key,value);
	}
}



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