http協議和web應用有狀態和無狀態淺析 
我們通常說的web應用程序的無狀態性的含義是什么呢?
直觀的說,“每次的請求都是獨立的,它的執行情況和結果與前面的請求和之后的請求是無直接關系的,它不會受前面的請求應答情況直接影響,也不會直接影響后面的請求應答情況”
要明白,這句話的含義是指在說明,http協議作為技術背景的web應用程序請求——應答模式是無狀態的,這個事實基本不會發生改變,也不會因為加入cookies、session機制而變成有狀態的。要明白,這種前后因果關系:“我們要實現的是一種web應用,實現這種應用的協議我們選擇了http這種本質上是無狀態的通信協議。但是事實上,我們需要我們的web應用是有狀態的。所以我們加入了cookies、session等機制去實現由狀態的web應用”。所以我們可以這么理解:
Web應用=http協議+session、cookies等狀態機制+其他輔助的機制。
其實,應用程序(軟件通信)的有狀態與否是一個非常通用的概念。我們可知,在網絡協議中,我們稱TCP為一個有狀態的傳輸層通信協議,而UDP則不是;IP是無狀態的。要明白這種有狀態與否的判定,是指你這一協議棧層次所要實現的功能——是否由上下文決定——來判定的(是否受之前的通信過程直接影響、是否直接影響之后的通信過程)
IP層以下的我們就不思考了。我們做一個網絡應用,需要使用網絡協議。其實按照原理上講,標准的TCP/IP協議族提供給我們的應用層協議(FTP, HTTP)不能直接的被稱為應用,因為在實現某種可用的、直接面向用戶的應用的時候(如web應用,人們可以上網),只有http協議還是不夠的。所以我們可以這么理解。網絡標准協議分層中提供給我們的應用層協議,它更像是一種分類。自然界的應用可能是無窮盡的種類,但是根據他們的特點、傳輸的特色,標准的網絡協議在傳輸層(通用網絡協議)的基礎上封裝出若干種面向不同種類網絡應用的協議。
某種角度上講,我們想要實現某種可用的網絡應用,直接使用網絡協議的傳輸層給我們提供的接口就可以了(也就是socket接口),但是有時候,這種方式是有些麻煩的,所以我們還是根據你要實現的web應用,在已有的標准協議中提供的面向應用分類的協議中進行選擇。這樣可以免去那些繁瑣的、通用的工作。
可以看到,我們實際生活中的有關網絡的應用程序,與標准的網絡通信協議提供給我們的應用層協議是沒有絕對的對應關系。所以標准的網絡通信協議給我們提供的應用層協議,只是提供給我的一種“建議的”分類。建議你:“如果你要實現這樣的應用,你可以直接使用這個封裝協議,而不是socket接口”。
我們再看看,前面一副圖中,所給我們的關於網絡應用層次中的各層次的有無狀態情況。可以知道,支持協議(下層)的有無狀態,消費協議(上層)的有無狀態,沒有直接的關系。還是那句話,每層協議的有無狀態與它的本身功能執行的時候的有無狀態的特點有關。
(1)IP是無狀態的,它只負責將一個IP包發送到指定的IP地址上去。它不會考慮這個包與前面已經發送的包和后面的包的聯系。(可能是重發包、可能是不連續包,它不管)。
(2)TCP是有狀態的,它通過包頭中的一些控制字段(序列編碼等)來表明各個包之間的關系(前后關系,重包與否等等)。所以,通過這個協議你可以做到一個可靠的傳輸。那么TCP是面向連接的協議是什么意思呢?其實這里的面向連接其實就是“三次握手”。三次握手,首先可以保證對方的存在,其次握手的所交換的內容是為將來進行有狀態的傳輸做准備。
(3) UDP是無狀態的,它僅僅是在IP上加了Port,其他的事情什么也不干。這樣它不可能做到可靠的傳輸,同樣也不需要連接。
(4) HTTP是無狀態的,它的底層協議是由狀態的TCP,但是HTTP的一次完整協議動作,里面是使用有狀態的TCP協議來完成的。而每次協議動作之間沒有任何關系。例如:第7次請求HTTP協議包,並不知道,這個包是為了什么?它或許是因為上次沒有請求成功而重傳,或許是上次的后續請求,或許是其他的,這些HTTP自身都不知道。
(5) www應用,很多時候,www應用是需要每個HTTP請求或應答動作之間是有關聯的,那就是使應用有狀態。這樣才能提供給用戶最好的用戶體驗。
於是,問題就來了,為什么當初HTTP會設計成無狀態的,既然現在我們所需要的www應用是有狀態的,為什么給他提供的這樣的底層協議是無狀態的。我想這個問題,可以從歷史的角度去思考。在www應用還很簡單的時候,這個應用只是被用來瀏覽內容。如果只是瀏覽內容的話,無狀態的協議已經夠了,這樣實現可以減輕實現的負擔,因為有狀態的協議實現起來代價相對來說是很高的(要維護狀態,根據狀態來處理情況,這就是為什么建議你可以不用session的時候就不用,因為服務器要給你負擔起很多的東西,例如內存空間啊)。好,現在看來,似乎www 應用是大部分需要狀態了,那么是否我們就應該改變這個協議來讓他變成一個有狀態的協議呢?從這個角度上講,我認為是不應該的。
首先,web應用與文件傳輸是不同的,文件傳輸,從開始到結束是一個“盡可能做完”的動作,所以這類動作不會在資源占有上,浪費它不該浪費的東西。而web應用中,用戶可能訪問一個頁面后,在那個頁面上逗留很久才跳轉到另外一個頁面,如果你需要我們在這兩個頁面(兩個http請求應答)之間維持狀態,是非常代價高的。
其次,歷史讓http無狀態,而應用需求對http提出有狀態的要求,按照軟件領域的通常做法是,保持歷史遺留的經驗(不在http協議本質上作太大的改動),兼容過去的軟件。在http上再加上一層來實現我們的目的( “再加上一層,你能做任何事”)。這一層,就是cookies,就是session等。
最后總結,http協議仍然保持無狀態,其充分的理由,並且,想要基於http協議的web應用變得有狀態,實現起來並不麻煩。
web應用都有哪些方法來讓應用有狀態
於是就引出了,在http協議的基礎上,web應用引入cookies, session, application。這樣的東西來保持web應用之間的狀態。
可知,cookies, session,application都不是標准協議,但是各種網絡應用提供商,實現語言、web容器等,都默認支持它。當然這種支持與對網絡標准協議的支持是不同的,標准協議規定的接口,而這種機制,只是規定了思想。就告訴你,大的概念上,jsp和ASP的session機制所要實現的功能和實現的方法不會有太大的出入。
有人將web應用中有無狀態的情況,比着顧客逛商店的情景。
顧客:瀏覽器訪問方;
商店:web服務器;
一次購買:一次http訪問
我們知道,上一次顧客購買,並不代表顧客下一個小時一定會買(當然也不能代表不會)。也就是說同一個顧客的不同購買之間的關系是不定的。所以說實在的,這種情況下,讓商店保存所有的顧客購買的信息,等到下一次購買可以知道這個顧客以前購買的內容代價非常大的。所以商店為了避免這個代價,索性就認為每次的購買都是一次獨立的新的購買。淺台詞:商店不區分對待老顧客和新過客。這就是無狀態的。
但是,商店為了提高收益。她是想鼓勵顧客購買的。所以告訴你,只要你在一個月內購買了5瓶以上的啤酒,就送你一個酒杯。
我們看看這種情況我們怎么去實現呢?
A,給顧客發放一個磁卡,里面放有顧客過去的購買信息。
這樣商店就可以知道了。這就是cookie.
B,給顧客發放一個唯一號碼,號碼制定的顧客的消費信息,存儲在商店的服務器中。這就是session。
最后,商店可以全局的決定,是5瓶為送酒杯還是6瓶。這就是application。
其實,這些機制都是在無狀態的傳統購買過程中加入了一點東西,使整個過程變得有狀態。Web應用就是這樣的。
注意,viewstate
其實從我們上面的分析看來,application不應該被視為這種意義上出現的維護狀態的機制。它是決定怎么應用程序的“配制文件”。但是如果你從這種狀態維持機制所覆蓋的范圍來推導,你會發現,application好像也算得上。
Session所控制的范圍是一個session。一個會話,會話從第一次訪問服務器開始存在,到服務器調用session.invalidator()(可能是超時,可能是其它原因)。
Cookies所控制的范圍有它自己的定義(與session沒有直接的關系),可以長可以短。只要服務器放在用戶文件系統中的cookies沒有被刪除,至少服務器還識別它。它的控制范圍就是還在的。
這個角度上講,Session和Cookies都可以歸為跨頁面的狀態。但是session跨不出一次會話,Cookies跨不出兩端的限制。
Application,則是關聯這個網絡應用程序的。
除了這三種狀態機制,有些網絡應用的特別實現還各自特殊的東西。像ASP.net的viewstate。這個東西,其實就是使用input type=”hiden”的東西來實現的。再加上web服務器那邊輔助,就可以實現,一個頁面在“回傳”的時候還能保持上一次的頁面狀態。
其實,這里要說明的就是“回傳”的意思,事實上,無狀態的http協議下,是沒有所謂的回傳的概念的。這里的回傳,情景可以這么理解。
我在客戶端填寫一定的數據(在文本框中、密碼框中、下拉列表中)。然后觸發一個訪問服務器的動作,例如:提交。這個時候服務器接受到我的請求然后又返回剛才這個頁面給我們。這種情景我們很常見(輸入用戶名密碼,點擊“登陸”,服務器端判斷密碼不對,就返回登陸頁面,但是之前我們填


