網絡協議系列之二:HTTP(2)

這篇文章繼續對HTTP協議進行說明。

一個http事務包括客戶向HTTP服務器發送請求以及服務器向客戶端發送響應兩個過程。根據事務的特點,這兩個過程具有原子性,任何一個過程失敗都會進行回滾(恢復事務之前的狀態)。從請求與響應的角度出發,http的知識點都是圍繞這兩個角度展開的。每次客戶的請求都會向服務器發送一個請求報文,請求報文的結構包括請求行、首部行、空行和請求體,一個http響應包括響應行、首部行、空行和響應體。http是基於TCP協議之上的應用層協議,默認使用80端口進行傳輸,也有基於SSL/TSL協議的https協議,默認使用443端口進行傳輸。
http協議雖然是基於TCP傳輸協議的,但確實無狀態的,就是說每次完成一個事務後,客戶端與服務器之間的連接就會斷開,服務器不保存任何客戶端的信息。但是人們發現無狀態會降低使用體驗,最直觀的例子就是當我們登陸一個網站不小心退出向再次登陸的時候,因爲http是一種無狀態協議,所以用戶的登陸信息需要重新輸入,顯然很影響使用體驗,於是人們就想出了後來我們耳熟能詳的Cookie和Session(後面還會提到)。

http協議到目前有兩個版本,http1.0和http1.1,現在目前普遍再使用的是http1.1的版本。http1.1在http1.0的基礎增加以下特性:

http1.1默認支持長連接
http1.1在消息頭中增加Host域
http1.1增加了100、101、203等響應狀態碼
http1.1增加OPTIONS,PUT,DELETE,TRACE,CONNECT這些Request方法
http1.1使用了更完整的緩存機制(後面還會提到)
下面就從請求與響應兩個角度說明http協議的方方面面

http請求

前面提到http協議(這裏介紹的都是基於http1.1版本)請求會發送給一個請求報文,我們從這個請求報文入手,詳細說明http請求是什麼鬼,下面是一個http請求的模板:
這裏寫圖片描述
其中sp表示空格,cr表示回車,lf表示換行,注意到從請求行到主體,每一行都包括回車和換行,中間的空行是爲了區分請求頭和請求主體的。空行之前的都是請求頭,空行之後的是具體的請求消息主體。

請求方法

對方法字段的說明如下:

GET:從服務器請求文檔
HEAD:請求關於這個文檔的信息,然並不是文檔自身
POST:從客戶端向服務器發送消息
PUT:服務器向客戶端發送文檔
TRACE:回顯服務器收到的請求,主要用於測試或診斷
CONNECT:預留給能夠將連接改爲管道方式的代理服務器
DELETE:刪除Web網頁
OPTIONS:詢問可用的選項
URL就是在瀏覽器中提交的地址信息,版本則是http1.1

請求首部行(也稱爲請求頭)

注意到首部行中可以包含多個行,每個首部行都是首部名陳與值的組合,那麼首部名稱有哪些呢?

Accept:給出客戶能夠接收的媒體類型。例如Accept: text/plain, text/html
User-agent:標誌客戶程序的相關信息。例如User-Agent: Mozilla/5.0 (Linux; X11)
Accept-Charset:給出客戶能夠接受的字符集。例如Accept-Charset: iso-8859-5
Accept-Encoding:給出客戶能夠處理的編碼方案。例如Accept-Encoding: compress, gzip
Accept-Language:給出客戶能夠接受的語言類型。例如Accept-Language: en,zh
Authorization:HTTP授權的授權證書。例如Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Host:給出主機地址和端口號。例如Host: www.baidu.com
Upgrade:指明優先使用的通信協議(如果支持的話)。例如Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
Cookie:把Cookie回送給服務器。例如Cookie: $Version=1; Skin=new;
If-Modified-Since:如果請求的部分在指定時間之後被修改則請求成功,未被修改則返回304代碼。例如If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT
Connection:表示是否需要持久連接。(HTTP 1.1默認進行持久連接)。例如Connection: close表示不使用長連接
Cache-Control:指定請求和響應遵循的緩存機制。例如Cache-Control: no-cache表示不使用緩存
Content-Length:請求的內容長度
Content-Type:請求的與MIME對應的類型。例如Content-Type: application/x-www-form-urlencoded
Pragma:用來包含實現特定的指令
請求實體

這裏面的內容就是客戶端實際請求的內容了,通常是一些備註信息

http響應

響應就是服務器給客戶端發送的數據,是根據請求而來的。這一點http比較死板,因爲沒有收到來自客戶單的請求服務器就不會主動給客戶端發送響應數據。一個標準的響應報文通常包括狀態行、響應首部行、空行和響應主體構成。下面是一個標準的HTTP響應報文模板:
這裏寫圖片描述
狀態行

狀態行由三個字段構成:版本號標示了當前使用的HTTP協議的版本目前是http1.1,狀態碼由三位的十進制數字構成,後面會詳細說明各個狀態碼的意義,短語是狀態碼的補充說明信息。在HTTP協議1.1版本中,共有5類41個狀態碼,下面是一些常用的狀態碼:

1** 信息,服務器收到請求,需要請求者繼續執行操作
2** 成功,操作被成功接收並處理
3** 重定向,需要進一步的操作以完成請求
4** 客戶端錯誤,請求包含語法錯誤或無法完成請求
5** 服務器錯誤,服務器在處理請求的過程中發生了錯誤
下面是常見狀態碼的補充說明:

100:短語是Continue。請求的開始部分已經收到,客戶可以繼續它的請求
101:Switching protocol。服務器同意切換協議,只能切換到更高版本的協議
200:OK。請求成功
201:Created。成功請求並創建了新的資源
202:Accepted。已接受請求,但是還沒有處理完成
203:Non-Authoritative Information,非授權信息。請求成功,但返回的meta信息不在原始的服務器,而是一個副本
204:No Content。無內容,服務器成功處理,但未返回內容。在未更新網頁的情況下,可確保瀏覽器繼續顯示當前文檔
301:Move Permanently。永久重定向,請求的資源已被永久的移動到新URI,返回信息會包括新的URI,瀏覽器會自動定向到新URI。今後任何新的請求都應使用新的URI代替
302:Found。臨時移動。與301類似,但資源只是臨時被移動。客戶端應繼續使用原有URI
304:Not Modified。未修改,所請求的資源未修改,服務器返回此狀態碼時,不會返回任何資源。客戶端通常會緩存訪問過的資源,通過提供一個頭信息指出客戶端希望只返回在指定日期之後修改的資源
400:Bad Request。客戶端請求的語法錯誤,服務器無法理解
401:Unauthorized。請求要求用戶的身份認證
403:Forbidden。服務器理解請求客戶端的請求,但是拒絕執行此請求
404:Not Found。未找到,服務器無法根據客戶端的請求找到資源(網頁)。通過此代碼,網站設計人員可設置”您所請求的資源無法找到”的個性頁面
405:Method Not Allowed。客戶端請求中的方法被禁止
406:Not Acceptable。服務器無法根據客戶端請求的內容特性完成請求
500:Internal Server Error。服務器內部錯誤,無法完成請求
501:Not Implemented:服務器不支持請求的功能,無法完成請求
503:Service Unavailable。由於超載或系統維護,服務器暫時的無法處理客戶端的請求。
響應首部行

Date:給出當前日期
Upgrade:指明優先使用的協議(如果可用的話)
Server:給出服務器的相關信息。例如Server: Apache/1.3.27 (Unix) (Red-Hat/Linux)
Set-Cookie:設置http Cookie。例如Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
Accept-Ranges:服務器可接受的字節範圍
Last-Modified:請求的資源的最後修改時間。例如Last-Modified: Tue, 15 Nov 2010 12:45:26 GMT
ETag:請求變量的實體標籤的當前值。例如ETag: “737060cd8c284d8af7ad3082f209582d”
Expires:響應過期的日期和時間
Location:請求客戶將請求發送另一個站點。例如Location: http://www.zcmhi.com/archives/94.html
Cache-Control:緩存機制的類型
Content-Encoding:web服務器支持的返回內容壓縮編碼類型。
Content-Language:響應體的語言
Content-Length:響應體的長度
Content-Type:返回內容的MIME類型
響應主體

如果不是錯誤的報文,這裏的內容是服務器給客戶端發送的文檔

由於HTTP是基於TCP協議的,所以當我們輸入一個網址按回車後,也會經歷三次握手,然後開始傳輸數據。TCP的三次握手已經在前面的文章詳細介紹了,這裏就不再贅述,這裏要說的是長連接(在TCPIP協議族中叫持續連接,都一樣)。長連接通過在Connection設置值爲keep-alive就可以實現長連接,不過由於在HTTP1.1的版本中已經默認支持了,所以設置也是多餘,但是經過抓包發現,雖然是默認支持的,但是請求頭中仍然保留了這個設置。使用長連接後,客戶端與服務器可以在同一條連接中操作多個事務,提高傳輸的效率,同時也降低了資源的開銷。

Cookie

Cookie是由服務器創建並保存在客戶端的小文件,其內容通常是鍵值的形式。其主要解決的是http協議無狀態這一問題的,通過使用cookie,每次客戶端的請求都會把cookie的信息發給服務器,由於cookie文件本來就是由服務器創建的,所以如果發現客戶端的請求已經在cookie中出現過,那麼就會從cookie中發送響應給客戶端,這樣就相當於記住了這個請求,還是上面的例子,我們以登陸淘寶網爲例,看看cookie是怎麼運行的:

下面是我第二次登陸淘寶產生的請求報文:

POST /member/request_nick_check.do?_input_charset=utf-8 HTTP/1.1
x-requested-with: XMLHttpRequest
Accept-Language: zh-cn
Referer: https://login.taobao.com/member/login.jhtml
Accept: application/json, text/javascript, */*; q=0.01
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host: login.taobao.com
Content-Length: 822
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: _umdata=AEEAFDDAE83E4B00CA26803C9421B97EAE678B97A4115D92D89F741A086801F18EEF2C6E67DABD4A9190B085E75633A9F934D879D43962A8B0382F45BAC9B5426DA54C37E8208E0C; lid=%E6%84%A4%E6%80%92%E7%9A%84%E5%86%AC%E8%87%B3%E9%A5%BA00; lc=Vy0WoxbX7NpINANgekCtYw%3D%3D; v=0; cookie2=2c2bbad4a87b1888fab6a665d85a8707; _tb_token_=ggD20qBz0u0pkz3; uc1=cookie14=UoWzXcxBeVjymA%3D%3D&existShop=false&cookie16=WqG3DMC9UpAPBHGz5QBErFxlCA%3D%3D&cookie21=W5iHLLyFeYZ1WM9hVnmS&tag=2&cookie15=URm48syIIVrSKA%3D%3D&pas=0; existShop=MTQ0NDgxMDE4Mw%3D%3D; sg=062; cookie1=B0OqfY0RHVr%2FOcx9qBvroWb9XgI8Di0Qd%2BuC3PoFnEk%3D; unb=1863540266; skt=1baf89c3fabaf276; _l_g_=Ug%3D%3D; _nk_=%5Cu6124%5Cu6012%5Cu7684%5Cu51AC%5Cu81F3%5Cu997A00; cookie17=UondFzxqdRam2Q%3D%3D; t=1e9865b82d621d88c434e1eafa0e3756; cna=kPukDggEeVQCAbfPvQ7eJp+f; isg=0A6DCA6FDD8F2080179659CB403CD682; l=AoaGaE8yxBlkra71oB90h7kflnIJc8qj; thw=cn; uc3=nk2=1BgxApbvq5DWyJqZEM8%3D&id2=UondFzxqdRam2Q%3D%3D&vt3=F8dASMh8gmM6QZZWUV8%3D&lg2=U%2BGCWk%2F75gdr5Q%3D%3D; lgc=%5Cu6124%5Cu6012%5Cu7684%5Cu51AC%5Cu81F3%5Cu997A00; tracknick=%5Cu6124%5Cu6012%5Cu7684%5Cu51AC%5Cu81F3%5Cu997A00; mt=ci=51_1; _cc_=UIHiLt3xSw%3D%3D; tg=0

username=&ua=244UW5TcyMNYQwiAiwQRHhBfEF8QXtHcklnMWc%3D%7CUm5OcktyS35Cf0B%2BR35Cfig%3D%7CU2xMHDJxPk82UjVOI1h2VnhNY0NtMVA2Wj1DORdBFw%3D%3D%7CVGhXd

下面是服務器返回的響應報文:

HTTP/1.1 200 OK
Server: Tengine
Date: Wed, 14 Oct 2015 08:10:46 GMT
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Content-Length: 18
Connection: keep-alive
S: STATUS_NORMAL
X-Category: 
Expires: Thu, 01 Jan 1970 00:00:01 GMT
Cache-Control: no-cache
Pragma: no-cache
Cache-Control: no-store

{"needcode":false}

從上面的響應報文中可以發現響應體並沒有內容,那返回的內容是哪裏來的呢?自然是從Cookie中獲取併發送給瀏覽器的。通過這個實例可以發現服務器通過在客戶端存儲Cookie達到記住客戶的目的,從而大大提高了網站的響應速度。還有一點,Cookie並不是與具體的web頁面關聯的,Cookie只與網站關聯,所以在一個網站的每個頁面的不同操作如果在用戶沒有禁止Cookie的情況下,服務器會把所有的操作都記錄在Cookie文件中,如果客戶的操作記錄不再Cookie中,服務器會把新的操作記錄更新到Cookie文件中,只要Cookie文件沒有過期,網站就會記住你在這個網站的所有操作記錄或者叫點擊記錄。

現在已經知道Cookie是通過在客戶端保存用戶信息來達到服務器記住用戶的目的的,還有一種通過把客戶信息保存在服務器的方式記住客戶的方法,這就是Session

Session

客戶端瀏覽器訪問服務器的時候,服務器把客戶端信息以某種形式記錄在服務器上。客戶端瀏覽器再次訪問時只需要從該Session中查找該客戶的狀態就可以了。Session對象也是客戶端在第一次請求服務器創建的,Session對象的存儲形式也是鍵值對,在服務器可以通過request.getSession().setAttribute(“name”,”value”)設置Session,而在客戶端可以通過request.getSeesion().getAttribute(“name”)的方式獲取session的value。雖然Session是保存在服務器端的,但是也需要客戶端瀏覽器的支持,http協議由於是無狀態的,所以Session對象不能TCP連接就確認是否是同一個客戶,所以服務器向客戶端發送一個名爲JSESSIONID的Cookie並保存在客戶端,value就是HttpSession.getId()方法返回的值,這樣在客戶端就保存了該Session對象Session id。該Session id的值的有效期僅對當前瀏覽器有效,因爲更具體的說Session id的值是保存在內存中的,所以關閉瀏覽器Session id就會失效。因爲Session從語義上理解就是回話的意思,所以會話結束Session自然就失效了。但要注意的是,儘管Session僅對當前瀏覽器有效,但是對從當前窗口打開的子窗口仍然是有效的,就是說父子頁面之間的Session對象是可以共享的。

Session對象和Cookie一樣也是有有效期的,從上面已經知道在同一個瀏覽器打開連個窗口會產生兩個Session,那麼如果打開的窗口越來越多肯定佔用的內存會越來越大,而長期不活躍的Session不過不及時刪除的話就很容易造成內存溢出。Session的超時時間爲maxInactiveInterval屬性,可以通過對應的getMaxInactiveInterval()獲取超時時間,通過setMaxInactiveInterval(longinterval)修改。這個超時時間就保證了不活躍的Session會被及時刪除,降低發生內存溢出的可能性。

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