Web 分散式系統使用的通訊協定並不多:傳統 Web 系統以 HTTP 為標準的訊息交換協定;Web 服務則以 SOAP 為預設的訊息交換方式。本節詳細討論這兩個協定。
超文本傳輸協定(HTTP)#
Web 上客戶端與伺服器之間的所有通訊都基於超文本傳輸協定(Hypertext Transfer Protocol, HTTP)。HTTP 是相對簡單的客戶端—伺服器協定:客戶端送出請求訊息,等待回應訊息。
HTTP 的重要性質是無狀態(stateless):它沒有「開啟中連線」的概念,也不要求伺服器維護客戶端的資訊。
HTTP 連線#
HTTP 建立在 TCP 之上:客戶端發請求前先對伺服器建立 TCP 連線,在該連線上送出請求並接收回應。以 TCP 為底層協定,HTTP 不必擔心請求或回應遺失——雙方可以假設訊息會送達對方;若真的出錯(連線中斷、逾時),會回報錯誤,但一般不會嘗試從失敗中復原。
早期 HTTP 的一個問題是 TCP 連線使用得沒有效率。每份 Web 文件是由同一伺服器上的一組不同檔案構成的,要正確顯示文件,這些檔案也得傳給客戶端,而每個檔案原則上都是客戶端可以另外發請求索取的另一份文件:
- 非持久連線(nonpersistent connections):HTTP 1.0 及更早版本中,對伺服器的每個請求都要另建一條連線,伺服器回應後連線就拆掉。主要缺點是建立 TCP 連線相對昂貴,把整份文件連同其所有元素傳給客戶端可能花上可觀的時間。
- HTTP 並不禁止客戶端對同一伺服器同時建立多條連線——許多瀏覽器用這招隱藏連線建立時間造成的延遲、並平行傳輸資料以改善效能。
- 持久連線(persistent connection):HTTP 1.1 的做法——同一條連線可以承載多個請求與各自的回應,不必為每組(請求, 回應)另建連線。客戶端還可以連續發出多個請求而不等第一個回應,稱為管線化(pipelining),進一步改善效能。

圖 12-10:(a) 每個參照各用一條 TCP 連線。(b) 使用持久連線。
HTTP 方法#
HTTP 被設計為通用的客戶端—伺服器協定,支援文件雙向傳輸。客戶端把想執行的操作放在請求訊息裡送給伺服器。最常用的操作:
- head:只要文件的中繼資料(metadata),不要文件本身。例如取得文件的修改時間,用來驗證客戶端快取副本的有效性;也可用來確認文件是否存在而不必實際傳輸。
- get:最重要的操作——從伺服器抓取文件回傳給客戶端。可以指定只在文件於特定時間之後被修改過才回傳;HTTP 也允許文件帶關聯標籤(字串),只在符合特定標籤時才抓取。
- put:get 的相反——請求伺服器以指定名稱(隨請求送出)儲存一份文件。伺服器一般不會盲目執行 put,只接受授權客戶端的這類請求。
- post:與儲存文件類似,但客戶端請求的是把資料加進某份文件或文件集合,典型例子是對新聞群組張貼文章。與 put 的區別:post 指明文章要「加到」哪一組文件,文章隨請求送出;put 則帶著文件與伺服器應存放它的名稱。
- delete:請求伺服器移除訊息中指名的文件。是否真的刪除取決於各種安全措施——甚至可能伺服器自己就沒有刪除該文件的權限,畢竟伺服器也只是一個使用者行程。

圖 12-11:HTTP 支援的操作
HTTP 訊息#
HTTP 只有請求與回應兩種訊息:
- 請求訊息包含三部分,其中**請求列(request line)**必填:指出客戶端要伺服器執行的操作、關聯文件的引用,以及客戶端期望的 HTTP 版本,另可附上額外的訊息標頭與內容。
- 回應訊息以狀態列(status line)開頭:包含版本號與三位數的狀態碼(status code),並附上簡短說明片語。例如狀態碼 200 表示請求可以達成,對應片語 “OK”;其他常見的有 400(Bad Request)、403(Forbidden)、404(Not Found)。

圖 12-12:(a) HTTP 請求訊息。(b) HTTP 回應訊息
請求或回應可附帶額外的訊息標頭(message headers),舉幾個例子:
- 客戶端對唯讀文件請求 post 操作時,伺服器會回覆狀態碼 405(“Method Not Allowed”),並附 Allow 標頭列出允許的操作(如 head 與 get)。
- 客戶端只想要 T 時間之後修改過的文件時,get 請求附上 If-Modified-Since 標頭並給定 T。
- 客戶端能接受 gzip 壓縮過的回應時,送出內容為 “Accept-Encoding:gzip” 的 Accept-Encoding 標頭;Accept 標頭則可指定例如只接受 HTML 網頁。
- Location 與 Referer 用於把客戶端重導向到另一份文件(規格書裡 “Referer” 是拼錯的),對應第 5 章解釋過的以轉遞指標(forwarding pointers)定位文件:客戶端請求文件 D 時,伺服器可能以 Location 標頭回覆,指示客戶端改對文件 D’ 重發請求;客戶端引用 D’ 時可加上含 D 引用的 Referer 標頭,說明重導向的來由。一般而言此標頭用來表示客戶端最近一次請求的文件。
- Upgrade 用於切換到另一個協定:例如客戶端與伺服器一開始只用 HTTP/1.1 作為建立連線的通用方式,伺服器可立即回覆內容為 “Upgrade:SHTTP” 的 Upgrade 標頭,表示要改用安全版本的 HTTP 繼續通訊。
- 另有兩個安全相關的標頭,但 Web 安全通常交由獨立的傳輸層協定處理(見「安全」一節)。

圖 12-13:一些 HTTP 訊息標頭
簡單物件存取協定(SOAP)#
HTTP 是傳統 Web 分散式系統的標準通訊協定,**簡單物件存取協定(Simple Object Access Protocol, SOAP)**則是 Web 服務通訊的標準。SOAP 讓 HTTP 變得更加重要:多數 SOAP 通訊都透過 HTTP 實作。
SOAP 本身不是困難的協定,主要目的是提供相對簡單的手段,讓彼此可能所知甚少的不同參與方得以通訊——協定的設計假設就是通訊雙方的共同知識非常少。基於這個假設,SOAP 訊息大量以 XML 為基礎並不意外:
- XML 是元標記語言,XML 描述本身就包含「用來描述文件的元素」的定義。實務上這表示訊息所用語法的定義是訊息的一部分,讓接收方能剖析非常不同型態的訊息。
- 當然,訊息的語意仍未定義——收到訊息該採取什麼動作也是。若接收方無法理解訊息內容,仍然無法有任何進展。
SOAP 訊息的結構:
- 訊息一般由兩部分組成,一起放進 SOAP 信封(envelope):**本體(body)**裝實際訊息;**標頭(header)**是選配的,裝與傳送路徑上節點相關的資訊——這些節點通常是 Web 服務多層式實作中的各個行程。信封內一切(標頭與本體)都以 XML 表達。
- 看似奇怪的是,SOAP 信封不含接收者位址。SOAP 明確假設接收者由傳輸訊息的協定指定,為此 SOAP 定義了對底層傳輸協定的繫結(binding)。目前有兩種:繫結到 HTTP 時接收者以 URL 指定;繫結到 SMTP(網際網路郵件傳輸協定)時接收者以電子郵件位址指定。

圖 12-14:以 XML 為基礎的 SOAP 訊息範例
兩種繫結也對應兩種互動風格:
- 會話式交換(conversational exchange style):最常見——雙方本質上是在交換結構化文件。例如一份文件是電子訂機票時填的完整訂購單,回應則是確認文件,內含訂單編號、航班資訊、座位,也許還有登機時要掃的條碼。
- RPC 式交換(RPC-style exchange):較貼近呼叫 Web 服務時傳統的請求—回應行為——SOAP 訊息明確指出要呼叫的程序與輸入參數值清單,回應則是包含呼叫結果的正式訊息。
典型上 RPC 式交換以 HTTP 繫結支援,會話式訊息則繫結到 SMTP 或 HTTP;但實務上多數 SOAP 訊息都走 HTTP。
XML 把語法定義放進訊息,確實讓通用剖析器容易許多,但 XML 語法本身極其冗長:實務上剖析 XML 訊息常成為嚴重的效能瓶頸。奇怪的是,改善 XML 效能受到的關注相對很少(雖然已有解法在路上)。
旁論:XML 真的適合人讀嗎
同樣令人意外的是,許多人相信 XML 規格可以讓人類方便地閱讀。官方 SOAP 規格中的範例訊息,要弄清它到底在傳達什麼得費一番搜尋;不難想像「晦澀」往往是使用 XML 的自然副產品。於是問題浮現:XML 這種文字式做法是否正確——沒有人能方便地讀 XML 文件,剖析器又被嚴重拖慢。