再談Http協議中消息的編碼

以前大概知道URLencoded, application/x-www-form-urlencoded等大概的東西,也做了一些能運行的程序,可總歸不是特別清楚。今天又看到了HTTP: The Definitive Guide這本電子書,終於徹底明白了怎麼回事。

首先還是回顧一下HTTP協議的報文,Http協議報文有兩種,一是請求報文,二是響應報文,而這兩種報文格式和編碼規則是一樣的,所以這裡以請求報文為例。

請求報文(消息),由三個部分組成,從前到後分別是:

(1)start line

(2)headers

(3)body

好了,首先要明確的是(1)和(2)必須是ASCII碼字元,也就是說出現在(1)和(2)里的字元編碼必須為0-127之內。(3)中的內容可以是任何編碼,可以是字元編碼,也可以是圖像的編碼,也可以是任意二進位編碼。至於到底裡面是什麼,通過(2)中的 Content-Type:頭來說明。

總體格式是這樣的,(1)startline必須以CRLF結尾,CR,LF當然也是ASCII碼了。(2)headers也必須以CRLF結尾。需要注意的是,即使一個頭也沒有,仍需要一個CRLF表示頭的結束。

具體來說:

一、對於startline

method request-URL version CRLF,其中method為方法名,如GET,POST等,後跟空格,後跟請求的URL,後跟空格,後跟版本號,後跟CRLF。

這裡需要注意的是URL的編碼,前面已經講過了,首先startline里的內容必須為ASCII碼,而對於startline裡面的URL則更為苛刻,URL的格式為http://hostname:port/p1/p2/resource,其中://為固定編碼,/用來分隔路徑,:用來指定埠號,resource指定資源名,p1,p2是路徑名。URL的苛刻要求在於,hostname,p1,p2,resource的名稱必須限定於ASCII碼的一個子集,見下表:

Unreserved

[A-Za-z0-9] | "-" | "_" | "." | "!" | "~" | "*" | """ | "(" | ")"

Reserved

";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","

Escape

"%" <HEX> <HEX>

其中Reserved一行中的ASCII碼不允許出現在hostname,p1,p2,resource中,可要是確實需要這些字元怎麼辦呢,此時就需要通過稱為URLEncode的方法對不允許出現的字元進行編碼為允許的字元,例如本來resource的名字為~voice,那麼編碼後就變成了%7Evoice,其中7E為~字元的ASCII碼的十六進位的ASCII表示。原則上這種方式只能編碼Reserved的ASCII碼,而現在人們擴展了這種方法,也利用這種方法編碼複雜字元,如GB2312和UTF-8等,如把GB2312的「好人」編碼為%BA%C3%C8%CB,把UTF-8的"好人"編碼為%E5%A5%BD%E4%BA%BA,雖然這不規範但已經成了實事上的標準了。

二、對於headers

標頭的具體格式如下藍色所示:name: valueCRLF 其中,name為標頭表示變數的名字,後跟冒號,後跟一個可選的空格,後跟變數的值,後跟CRLF。

三、對於body

body里是什麼內容,如果是字元採用什麼編碼,如果是圖像又採用什麼格式,所有這些都是有headers里規定的。其中Content-Type規定了body裡面是什麼,採用什麼編碼,如Content-Type: text/html; charset=UTF-8,表示body里的內容是html文件,採用UTF-8編碼。這裡需要注意的是對於:Content-Type: application/x-www-form-urlencoded,這是POST常用的消息類型,它表明body里放的是表單數據,採用的編碼為urlencoded。首先,這種格式的body內容必須為ASCII碼,除了格式化字元自身外,其他字元必須限定於ASCII的unreserved子集。舉例來說,這種body的格式為name1=value1&name2=value2&name3=&name4=value4,name1,name2,name3,name4為變數名,value1,value2,value3,value4為變數的值,=和&為格式化字元。這裡要求name1,name2,name3,name4,value1,value2,value4的編碼必須為ASCII的UnReserved子集。

對編程的提示:

僅僅對需要URLEncode的地方進行編碼,不要全部進行編碼。如GET http://www.baidu.com/s?wd=~testCRLF。

(1)首先確定URL的部分,不是URL的部分根本不能進行URLencode編碼。顯然URL部分是http://www.baidu.com/s?wd=~test。對於GET,空格,CRLF三部分不屬於URL,不能進行特殊編碼。

(2)確定URL部分需要URLEncoded的地方。需要進行RULEncode的只是www.baidu.com,s,wd,~test。儘管www.baidu.com,s,wd編碼前後不變但它們也是需要編碼的部分。而對於http://,:,/,?,=,它們本身是格式化字元,具有特殊意義不能再進行URLencode編碼了。

所以startline可以這樣生成:

string startline = "GET " + "http://" + URLEncode(www.baidu.com) + "/" + URLEncode("s") + "?" + URLEncode("wd") + "=" + URLEncode("~test") + "/r/n";

千萬別寫成了

string startline = URLEncode("GET http://www.baidu.com/s?wd=~test/r/n");

也不要寫成

string startline = "GET " + URLEncode("htt://www.baidu.com/s?wd=~test") + "/r/n";


推薦閱讀:

丈夫手包里的那紙協議,讓我心如刀割
簽訂離婚協議時,不得不警惕的八個陷阱
不知道《離婚協議書》的這些陷阱,你可能會吃大虧
租房協議書怎樣寫
MS主叫流程 分析

TAG:編碼 | 協議 | 消息 |