HTTP初探

Web客戶端和服務器


瀏覽一個頁面時( 比如http://www.oreilly.com/index.html), 瀏覽器會向服務器www.oreilly.com 發送一條HTTP 請求(參見上圖)。服務器會去尋找所期望的對象(在這個例子中就是/index.html),如果成功,就將對象、對象類型、對象長度以及其他一些信息放在HTTP 響應中發送給客戶端。

媒體類型

因特網上有數千種不同的數據類型,HTTP 仔細地給每種要通過Web 傳輸的對象都打上了名爲MIME 類型(MIME type) 的數據格式標籤。最初設計MIME(Multipurpose Internet Mail Extension,多用途因特網郵件擴展)是爲了解決在不同的電子郵件系統之間搬移報文時存在的問題。MIME 在電子郵件系統中工作得非常好,因此HTTP 也採納了它,用它來描述並標記多媒體內容。
Web 服務器會爲所有HTTP 對象數據附加一個MIME 類型(參見圖1-3)。當Web瀏覽器從服務器中取回一個對象時,會去查看相關的MIME 類型,看看它是否知道應該如何處理這個對象。大多數瀏覽器都可以處理數百種常見的對象類型:顯示圖片文件、解析並格式化HTML 文件、通過計算機聲卡播放音頻文件,或者運行外部插件軟件來處理特殊格式的數據。


MIME 類型是一種文本標記,表示一種主要的對象類型和一個特定的子類型,中間由一條斜槓來分隔。


• HTML 格式的文本文檔由 text/html 類型來標記。
• 普通的 ASCII 文本文檔由 text/plain 類型來標記。
• JPEG 版本的圖片爲 image/jpeg 類型。
• GIF 格式的圖片爲 image/gif 類型。
• Apple 的 QuickTime 電影爲 video/quicktime 類型。
• 微軟的 PowerPoint 演示文件爲 application/vnd.ms-powerpoint 類型。


常見的MIME 類型有數百個,實驗性或用途有限的MIME 類型則更多。

報文

HTTP 報文是由一行一行的簡單字符串組成的。HTTP 報文都是純文本,不是二進
制代碼,所以人們可以很方便地對其進行讀寫。


HTTP 報文包括以下三個部分:
• 起始行
報文的第一行就是起始行,在請求報文中用來說明要做些什麼,在響應報文中說
明出現了什麼情況。
• 首部字段
起始行後面有零個或多個首部字段。每個首部字段都包含一個名字和一個值,爲
了便於解析,兩者之間用冒號(:)來分隔。首部以一個空行結束。添加一個首
部字段和添加新行一樣簡單。
• 主體
空行之後就是可選的報文主體了,其中包含了所有類型的數據。請求主體中包括
了要發送給Web 服務器的數據;響應主體中裝載了要返回給客戶端的數據。起
始行和首部都是文本形式且都是結構化的,而主體則不同,主體中可以包含任意
的二進制數據(比如圖片、視頻、音軌、軟件程序)。當然,主體中也可以包含
文本。

簡單的報文實例

下圖顯示了可能會作爲某個簡單事務的一部分發送的HTTP 報文。瀏覽器請求資源http://www.joes-hardware.com/tools.html。
在下圖中,瀏覽器發送了一條HTTP 請求報文。這條請求的起始行中有一個GET命令,且本地資源爲/tools.html。這條請求說明它使用的是1.0 版的HTTP 協議。請求報文沒有主體,因爲從服務器上GET 一個簡單的文檔不需要請求數據。
服務器會回送一條HTTP 響應報文。這條響應中包含了HTTP 的版本號(HTTP/1.0)、一個成功狀態碼(200)、一個描述性的原因短語(OK), 以及一塊響應首部字段,在所有這些內容之後跟着包含了所請求文檔的響應主體。Content-Length首部說明了響應主體的長度,Content-Type 首部說明了文檔的MIME 類型。


HTTP的基礎TCP/IP

我們來討論一下報文是如何通過傳輸控制協議(Transmission Control Protocol,TCP)連接從一個地方搬移到另一個地方去的。

HTTP 是個應用層協議。HTTP 無需操心網絡通信的具體細節;它把聯網的細節都交給了通用、可靠的因特網傳輸協議TCP/IP。


TCP 提供了:
• 無差錯的數據傳輸;
• 按序傳輸(數據總是會按照發送的順序到達);
• 未分段的數據流(可以在任意時刻以任意尺寸將數據發送出去)。


因特網自身就是基於TCP/IP 的,TCP/IP 是全世界的計算機和網絡設備常用的層次化分組交換網絡協議集。TCP/IP 隱藏了各種網絡和硬件的特點及弱點,使各種類型的計算機和網絡都能夠進行可靠地通信。
只要建立了TCP 連接,客戶端和服務器之間的報文交換就不會丟失、不會被破壞,也不會在接收時出現錯序了。


用網絡術語來說,HTTP 協議位於TCP 的上層。HTTP 使用TCP 來傳輸其報文數
據。與之類似,TCP 則位於IP 的上層



在HTTP 客戶端向服務器發送報文之前,需要用網際協議(Internet Protocol,IP)地址和端口號在客戶端和服務器之間建立一條TCP/IP 連接。


建立一條TCP 連接的過程與給公司辦公室的某個人打電話的過程類似。首先,要撥打公司的電話號碼。這樣就能進入正確的機構了。其次,撥打要聯繫的那個人的分機號。


在TCP 中,你需要知道服務器的IP 地址,以及與服務器上運行的特定軟件相關的TCP 端口號。


這就行了,但最初怎麼獲得HTTP 服務器的IP 地址和端口號呢?當然是通過URL了!我們前面曾提到過,URL 就是資源的地址,所以自然能夠爲我們提供存儲資源的機器的IP 地址。我們來看幾個URL:

http://207.200.83.29:80/index.html
http://www.netscape.com:80/index.html
http://www.netscape.com/index.html

第一個URL 使用了機器的IP 地址,207.200.83.29 以及端口號80。
第二個URL 沒有使用數字形式的IP 地址,它使用的是文本形式的域名,或者稱爲主機名(www.netscape.com)。主機名就是IP 地址比較人性化的別稱。可以通過一種稱爲域名服務(Domain Name Service,DNS)的機制方便地將主機名轉換爲IP地址,這樣所有問題就都解決了。
最後一個URL 沒有端口號。HTTP 的URL 中沒有端口號時,可以假設默認端口號是80。
有了IP 地址和端口號,客戶端就可以很方便地通過TCP/IP 進行通信了。下圖顯示了瀏覽器是怎樣通過HTTP 顯示位於遠端服務器中的某個簡單HTML 資源的。


步驟如下:
(a) 瀏覽器從URL 中解析出服務器的主機名;
(b) 瀏覽器將服務器的主機名轉換成服務器的IP 地址;
(c) 瀏覽器將端口號(如果有的話)從URL 中解析出來;
(d) 瀏覽器建立一條與Web 服務器的TCP 連接;
(e) 瀏覽器向服務器發送一條HTTP 請求報文;
(f) 服務器向瀏覽器回送一條HTTP 響應報文;
(g) 關閉連接,瀏覽器顯示文檔。


使用Telnet與TCP對話

由於HTTP 使用了TCP/IP 傳輸協議,而且它是基於文本的,沒有使用那些難以理解的二進制格式,因此很容易直接與Web 服務器進行對話。
Telnet 程序可以將鍵盤連接到某個目標TCP 端口,並將此TCP 端口的輸出回送到顯示屏上。Telnet 常用於遠程終端會話,但它幾乎可以連接所有的TCP 服務器,包括HTTP 服務器。
可以通過Telnet 程序直接與Web 服務器進行對話。通過Telnet 可以打開一條到某臺機器上某個端口的TCP 連接,然後直接向那個端口輸入一些字符。Web 服務器會將Telnet 程序作爲一個Web 客戶端來處理,所有回送給TCP 連接的數據都會顯示在屏幕上。
我們用Telnet 與一個實際的Web 服務器進行交互。我們要用Telnet 獲取URLhttp://www.joes-hardware.com:80/tools.html 所指向的文檔(你可以自己嘗試一下這個實例)。
我們來看看會發生什麼情況。
首先,查找www.joes-hardware.com • 的 IP 地址,打開一條到那臺機器端口 80 的TCP 連接。Telnet 會爲我們完成那些“跑腿兒”的工作。
• 一旦打開了 TCP 連接,就要輸入 HTTP 請求了。
• 請求結束(由一個空行表示)之後,服務器會在一條 HTTP 響應中將內容回送並關閉連接。
下例顯示了對http://www.joes-hardware.com:80/tools.html 的HTTP 請求實例。我們輸入的內容用粗體字表示。


Telnet 會查找主機名並打開一條連接,連接到在www.joes-hardware.com 的端口80上監聽的Web 服務器。這條命令之後的三行內容是Telnet 的輸出,告訴我們它已經建立了連接。
然後我們輸入最基本的請求命令GET/tools.html HTTP/1.1,發送一個提供了源端主機名的Host 首部,後面跟上一個空行,請求從服務器www.joes-hardware.com上獲取資源tools.html。隨後,服務器會以一個響應行、幾個響應首部、一個空行和最後面的HTML 文檔主體來應答。
要明確的是,Telnet 可以很好地模擬HTTP 客戶端,但不能作爲服務器使用。而且對Telnet 做腳本自動化是很繁瑣乏味的。


HTTP協議版本

現在使用的HTTP 協議有幾個版本。HTTP 應用程序要儘量強健地處理各種不同的HTTP 協議變體。目前仍在使用的版本如下。


• HTTP/0.9
HTTP 的1991 原型版本稱爲HTTP/0.9。這個協議有很多嚴重的設計缺陷,只應該用於與老客戶端的交互。HTTP/0.9 只支持GET 方法,不支持多媒體內容的MIME 類型、各種HTTP 首部,或者版本號。HTTP/0.9 定義的初衷是爲了獲取簡單的HTML 對象,它很快就被HTTP/1.0 取代了。


• HTTP/1.0
1.0 是第一個得到廣泛使用的HTTP 版本。HTTP/1.0 添加了版本號、各種HTTP首部、一些額外的方法,以及對多媒體對象的處理。HTTP/1.0 使得包含生動圖片的Web 頁面和交互式表格成爲可能,而這些頁面和表格促使萬維網爲人們廣泛地接受。這個規範從未得到良好地說明。在這個HTTP 協議的商業演進和學術研究都在快速進行的時代,它集合了一系列的最佳實踐。


• HTTP/1.0+
在20 世紀90 年代中葉,很多流行的Web 客戶端和服務器都在飛快地向HTTP中添加各種特性,以滿足快速擴張且在商業上十分成功的萬維網的需要。其中很多特性,包括持久的keep-alive 連接、虛擬主機支持,以及代理連接支持都被加入到HTTP 之中,併成爲非官方的事實標準。這種非正式的HTTP 擴展版本通常稱爲HTTP/1.0+。


• HTTP/1.1
HTTP/1.1 重點關注的是校正HTTP 設計中的結構性缺陷,明確語義,引入重要的性能優化措施,並刪除一些不好的特性。HTTP/1.1 還包含了對20 世紀90 年代末正在發展中的更復雜的Web 應用程序和部署方式的支持。HTTP/1.1 是當前使用的HTTP 版本。


• HTTP-NG(又名 HTTP/2.0)
HTTP-NG 是HTTP/1.1 後繼結構的原型建議,它重點關注的是性能的大幅優化,以及更強大的服務邏輯遠程執行框架。HTTP-NG 的研究工作終止於1998 年,編寫本書時,還沒有任何要用此建議取代HTTP/1.1 的推廣計劃。


TCP連接

世界上幾乎所有的HTTP 通信都是由TCP/IP 承載的,TCP/IP 是全球計算機及網絡設備都在使用的一種常用的分組交換網絡分層協議集。客戶端應用程序可以打開一條TCP/IP 連接,連接到可能運行在世界任何地方的服務器應用程序。一旦連接建立起來了,在客戶端和服務器的計算機之間交換的報文就永遠不會丟失、受損或失序。


TCP的可靠數據管道

HTTP 連接實際上就是TCP 連接和一些使用連接的規則。TCP 連接是因特網上的可靠連接。要想正確、快速地發送數據,就需要了解TCP 的一些基本知識。

TCP 爲HTTP 提供了一條可靠的比特傳輸管道。從TCP 連接一端填入的字節會從另一端以原有的順序、正確地傳送出來


TCP流是分段的、由IP分組傳送

TCP 的數據是通過名爲IP 分組(或IP 數據報)的小數據塊來發送的。

HTTP 要傳送一條報文時,會以流的形式將報文數據的內容通過一條打開的TCP 連接按序傳輸。TCP 收到數據流之後,會將數據流砍成被稱作段的小數據塊,並將段封裝在IP 分組中,通過因特網進行傳輸。所有這些工作都是TCP/
IP 軟件來處理的,HTTP 程序員什麼都看不到。


每個TCP 段都是由IP 分組承載,從一個IP 地址發送到另一個IP 地址的。每個IP分組中都包括:

• 一個 IP 分組首部(通常爲 20 字節);
• 一個 TCP 段首部(通常爲 20 字節);
• 一個 TCP 數據塊(0 個或多個字節)。


IP 首部包含了源和目的IP 地址、長度和其他一些標記。TCP 段的首部包含了TCP端口號、TCP 控制標記,以及用於數據排序和完整性檢查的一些數字值。


保持TCP連接的正確運行

在任意時刻計算機都可以有幾條TCP 連接處於打開狀態。TCP 是通過端口號來保持所有這些連接的正確運行的。


端口號和僱員使用的電話分機號很類似。就像公司的總機號碼能將你接到前臺,而分機號可以將你接到正確的僱員位置一樣,IP 地址可以將你連接到正確的計算機,而端口號則可以將你連接到正確的應用程序上去。TCP 連接是通過4 個值來識別的:


< 源IP 地址、源端口號、目的IP 地址、目的端口號>


這4 個值一起唯一地定義了一條連接。兩條不同的TCP 連接不能擁有4 個完全相同的地址組件值(但不同連接的部分組件可以擁有相同的值)


在下圖中,有4 條連接:A、B、C 和D。下表列出了每個端口的相關信息。




注意,有些連接共享了相同的目的端口號(C 和D 都使用目的端口號80)。有些連接使用了相同的源IP 地址(B 和C)。有些使用了相同的目的IP 地址(A 和B,C和D)。但沒有兩個不同連接所有的4 個值都一樣。


瀏覽器與SOCKET服務端的對話

socket server端代碼:


package com.shmec.socket;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;


public class SocketServer {
    public static void main(String[] args) throws IOException {  
        ServerSocket server = new ServerSocket(80);
        System.out.println("server:"+InetAddress.getLocalHost().getHostAddress()+" is already...");
        Socket client = server.accept();        
        BufferedReader in = new BufferedReader(new InputStreamReader(  
                client.getInputStream()));  
        PrintWriter out = new PrintWriter(client.getOutputStream());  
        while (true) {  
            String str = in.readLine();  
            System.out.println(str);
              
            if (str.equals("")){
            StringBuilder sb = new StringBuilder();
                sb.append("HTTP/1.1 200 OK").append("\n")
                .append("Content-Type: text/html").append("\n")
                .append("Content-Length: 433").append("\n")
                .append("").append("\n")
                .append("<HTML><HEAD><TITLE>Joe's Tools</TITLE></HEAD><BODY><H1>Tools Page</H1><H2>Hammers</H2></BODY></HTML>");
                System.out.println();
                System.out.println(sb.toString());
                out.println(sb.toString());  
                out.flush();
                break;
            }else if(str.equals("end")){
            break;
            }
               
        }  
        client.close();  
    }
}


服務端起來後,使用瀏覽器訪問:http://localhost/


發佈了16 篇原創文章 · 獲贊 16 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章