1、簡介
1.1、HTTP協議是什么?
即超文本傳輸協議(HTTP,HyperText Transfer Protocol)是互聯網上應用最為廣泛的一種網絡協議,所有的WWW文件都必須遵守這個標准。從網絡參考模型來看,它是屬於應用層。它規定了計算機通信網絡中兩台計算機之間進行通信所必須共同遵守的規定或規則,它允許將超文本標記語言(HTML)文檔從Web服務器傳送到客戶端的瀏覽器。
簡單的來說,它就是基於應用層一個規范一個標准!通訊雙發都需要遵守這一准則,這就是http協議!
1.2、http簡史
設計HTTP最初的目的是為了提供一種發布和接收HTML頁面的方法。1960年美國人Ted Nelson構思了一種通過計算機處理文本信息的方法,並稱之為超文本(hypertext),這成為了HTTP超文本傳輸協議標准架構的發展根基。Ted Nelson組織協調萬維網協會(World Wide Web Consortium)和互聯網工程工作小組(Internet Engineering Task Force )共同合作研究,最終發布了一系列的RFC,其中著名的RFC 2616定義了HTTP 1.1,這也是我們現在最常用的版本,在此之前還存在HTTP 1.0版本以及HTTP 0.9版本
2、URI與URL
問: 為什么要區別URI與URL呢?
答:因為我看書看博客資料都遇到過着兩個名詞,第一次遇到是在學習API的時候,那時候我是一臉懵逼,不是怎么區分,感覺看過去都是一串網址呀!事實並非如此。
URI:統一資源標示符,只是標識資源在哪里,這意味着存在多個URI可以指向該資源(例如:絕對與相對)【URI包含URL】
URI一般由三部分組成:
1. 訪問資源的命名機制。
2. 存放資源的主機名。
3. 資源自身的名稱,由路徑表示。
語法:[scheme:] scheme-specific-part
URI以scheme和冒號開頭。Scheme用大寫/小寫字母開頭,后面為空或者跟着更多的大寫/小寫字母、數字、加號、減號和點號。冒號把 scheme與scheme-specific-part分開了,並且scheme-specific-part的語法和語義(意思)由URI的名字空間決定。如下面的例子:
http://www.cnn.com,其中http是scheme,//www.cnn.com是 scheme-specific-part,並且它的scheme與scheme-specific-part被冒號分開了。
絕對與相對:
絕對的URI指以scheme(后面跟着冒號)開頭的URI。(例如:mailto:jeff@javajeff.com、news:comp.lang.java.help和xyz: //whatever);絕對的URI看作是以某種方式引用某種資源,而這種方式對標識符出現的環境沒有依賴。
相對的URI不是以scheme(后面跟着冒號)開始的URI。(例如:articles/articles.html、img/aa.jpg)你可以把相對的URI看作是以某種方式引用某種資源,而這種方式依賴於標識符出現的環境。(即你在html中引用圖片:./img/aa.jpg,那么它依賴的就是http)
URL:統一資源定位符,是URI的子集;它除了標識資源的位置,還提供一種定位該資源的主要訪問機制(如其網絡“位置”)。【即提供具體方式找到該資源(位置+方式)】
URL的格式由下列三部分組成:
1. 第一部分,是協議或稱為服務方式 (指定低層使用的協議,例如:http, https, ftp);
2. 第二部分,是存有該資源的主機IP地址(有時也包括端口號);
3. 第三部分,是主機資源的具體地址。如目錄和文件名等。
第一部分和第二部分之間用"://"符號隔開,第二部分和第三部分用"/"符號隔開。第一部分和第二部分是不可缺少的,第三部分有時可以省略。
3、TCP握手連接以及斷開(擴展)
TCP通信過程包括三個步驟:建立TCP連接通道,傳輸數據,斷開TCP連接通道。引用oneSong所畫的一張金典TCP通訊圖片
上圖中主要分為三部分:建立連接、傳輸數據、斷開連接。
建立連接:
三次握手即可建立TCP連接
1、第一次握手:客戶端發送syn包(seq=x)到服務器,並進入SYN_SEND狀態,等待服務器確認;
2、第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=x+1),同時自己也發送一個SYN包(seq=y),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
3、第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=y+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
握手過程中傳送的包里不包含數據,三次握手完畢后,客戶端與服務器才正式開始傳送數據。理想狀態下,TCP連接一旦建立,在通信雙方中的任何一方主動關閉連接之前,TCP 連接都將被一直保持下去。
為什么需要三次握手呢?
相互確認!(網上有很多解釋,這里就不多講了)
數據傳輸:
建立好連接后,開始傳輸數據。TCP數據傳輸牽涉到的概念很多:超時重傳、快速重傳、流量控制、擁塞控制等等。(這一切都是為了提供可靠的字節流服務)
斷開連接:
四次握手即可斷開TCP連接
1、第一次握手:主動關閉方發送一個FIN,用來關閉主動方到被動關閉方的數據傳送,也就是主動關閉方告訴被動關閉方:我已經不會再給你發數據了(當然,在fin包之前發送出去的數據,如果沒有收到對應的ack確認報文,主動關閉方依然會重發這些數據),但此時主動關閉方還可以接受數據。
2、第二次握手:被動關閉方收到FIN包后,發送一個ACK給對方,確認序號為收到序號+1(與SYN相同,一個FIN占用一個序號)。
3、第三次握手:被動關閉方發送一個FIN,用來關閉被動關閉方到主動關閉方的數據傳送,也就是告訴主動關閉方,我的數據也發送完了,不會再給你發數據了。
4、第四次握手:主動關閉方收到FIN后,發送一個ACK給被動關閉方,確認序號為收到序號+1,至此,完成四次揮手。
白話文:
1、第一次握手,瀏覽器對服務器說:“煞筆,我不再給你發數據啦,但可以接受數據。”
2、第二次握手,服務器對瀏覽器說:“騷貨,我知道啦!”
3、第三次握手,服務器對瀏覽器說:“騷貨,我也不再給你發數據啦!”
4、第四次握手,瀏覽器對服務器說:“煞筆,我知道啦!”
4、特點
HTTP協議永遠都是客戶端發起請求,服務器回送響應。這樣就限制了使用HTTP協議,無法實現在客戶端沒有發起請求的時候,服務器將消息推送給客戶端。、
主要特點:
1、支持客戶/服務器模式。一旦建立了運輸連接(這常常稱為建立了會話),瀏覽器端就向萬維網服務器端發送HTTP請求,服務器收到請求后給出HTTP響應。
2、簡單快速:客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST。每種方法規定了客戶與服務器聯系的類型不同。由於HTTP協議簡單,使得HTTP服務器的程序規模小,因而通信速度很快。
3、靈活:HTTP允許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type加以標記。
4、HTTP 0.9和1.0使用非持續連接:限制每次連接只處理一個請求,服務器處理完客戶的請求,並收到客戶的應答后,即斷開連接。HTTP 1.1使用持續連接:不必為每個web對象創建一個新的連接,一個連接可以傳送多個對象,采用這種方式可以節省傳輸時間。
5、無狀態:HTTP協議是無狀態協議。即每一個HTTP請求都是獨立的。萬維網服務器不保存過去的請求和過去的會話記錄。這就是說,同一個用戶再次訪問同一個服務器時,只要服務器沒有進行內容的更新,服務器的響應就給出和以前被訪問時相同的響應。服務器不記錄曾經訪問過的用戶,也不記錄某個用戶訪問過多少次。
5、HTTP請求
5.1、Request 消息的結構
請求消息的結構由三部分組成,請求行、請求頭、請求主體(即:請求行、消息報頭、請求正文。)
【請 求 行】請求方法 空格 請求資源地址(URI、無域名) 空格 HTTP版本 空格 CRLF(換行符)
【請 求 頭】標識:內容 CRLF(換行符)
【空 一 行】(表示請求頭結束)
【請求主體】(即請求正文,用戶的主要數據。POST方式時使用,GET無請求主體)
在HTTP/1.1 協議中,所有的請求頭,除Host外,都是可選的。
例:
GET /phpstudy2015-6/ HTTP/1.1 Host: www.cnblogs.com User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive If-Modified-Since: Sat, 06 May 2017 12:05:41 GMT
5.2、請求方法
HTTP/1.1協議中共定義了八種方法(有時也叫“動作”)來表明Request-URI指定的資源的不同操作方式,最基本的有4種,分別是GET,POST,PUT,DELETE。一個URL地址用於描述一個網絡上的資源,而HTTP中的GET, POST, PUT, DELETE就對應着對這個資源的查,改,增,刪4個操作。 我們最常見的就是GET和POST了。GET一般用於獲取/查詢資源信息,而POST一般用於更新資源信息。
【我們在瀏覽器地址欄直接輸入地址的時候,采用的就是GET方法】
各方法如下:
1、GET:向特定的資源發出請求
2、POST:向指定資源提交數據進行處理請求(例如提交表單或者上傳文件)。數據被包含在請求體中。POST請求可能會導致新的資源的建立和/或已有資源的修改。
3、PUT:向指定資源位置上傳其最新內容。
4、DELETE:請求服務器刪除Request-URI所標識的資源。
5、HEAD: 向服務器索要與GET請求相一致的響應,只不過響應體將不會被返回。這一方法可以在不必傳輸整個響應內容的情況下,就可以獲取包含在響應消息頭中的元信息。該方法常用於測試超鏈接的有效性,是否可以訪問,以及最近是否更新。
6、TRACE:請求服務器會送收到的請求信息,主要用於測試或診斷。
7、OPTIONS:請求查詢服務器的性能,或者查詢與資源相關的選項和需求
8、CONNECT: HTTP/1.1協議中預留給能夠將連接改為管道方式的代理服務器。(即留為將來使用)
【注意:請求方法區分大小寫;所示請求方法應為大寫】
GET與POST的區別:
1、GET提交的數據會放在URL之后,以?分割URL和傳輸數據,參數之間以&相連,如EditPosts.aspx?postid=6810130&update=1 ;POST方法是把提交的數據放在HTTP包的Body中。
2、GET提交的數據大小有限制(因為瀏覽器對URL的長度有限制),而POST方法提交的數據沒有限制。
3、GET方式需要使用Request.QueryString來取得變量的值,而POST方式通過Request.Form來獲取變量的值。
4、GET方式提交數據,會帶來安全問題,比如一個登錄頁面,通過GET方式提交數據時,用戶名和密碼將出現在URL上,如果頁面可以被緩存或者其他人可以訪問這台機器,就可以從歷史記錄獲得該用戶的賬號和密碼。
5.3、http的無狀態以及建立連接方式
無狀態:
http協議為了保證服務器的內存,不會維持客戶端發過來的請求,即同一個客戶端的這次請求和上次請求是沒有對應關系,對http服務器來說,它並不知道這兩個請求來自同一個客戶端。例如:一個瀏覽器在短短幾秒之內兩次訪問同一對象時,服務器進程不會因為已經給它發過應答報文而不接受第二期服務請求。
為了解決這個問題, Web程序引入了Cookie機制來維護狀態。
建立連接方式:
HTTP中支持兩種連接方式:非持久連接和持久連接(HTTP1.1默認的連接方式為持久連接)。
1、非持久連接方式(采用訪問例子來說明)
讓我們查看一下非持久連接情況下從服務器到客戶傳送一個Web頁面的步驟。假設該貝面由1個基本HTML文件和10個JPEG圖像構成,而且所有這些對象都存放在同一台服務器主機中。再假設該基本HTML文件的URL為:cnblogs.com/phpstudy2015-6/index.html。
下面是具體步騾:
1. HTTP客戶初始化一個與服務器主機cnblogs.com中的HTTP服務器的TCP連接。HTTP服務器使用默認端口號80監聽來自HTTP客戶的連接建立請求。
2. HTTP客戶經由與TCP連接相關聯的本地套接字發出—個HTTP請求消息。這個消息中包含路徑名/somepath/index.html。
3. HTTP服務器經由與TCP連接相關聯的本地套接字接收這個請求消息,再從服務器主機的內存或硬盤中取出對象/somepath/index.html,經由同一個套接字發出包含該對象的響應消息。
4. HTTP服務器告知TCP關閉這個TCP連接(不過TCP要到客戶收到剛才這個響應消息之后才會真正終止這個連接)。
5. HTTP客戶經由同一個套接字接收這個響應消息。TCP連接隨后終止。該消息標明所封裝的對象是一個HTML文件。客戶從中取出這個文件,加以分析后發現其中有10個JPEG對象的引用。
6.給每一個引用到的JPEG對象重復步騾1-4。
上述步驟之所以稱為使用非持久連接,原因是每次服務器發出一個對象后,相應的TCP連接就被關閉,也就是說每個連接都沒有持續到可用於傳送其他對象。每個TCP連接只用於傳輸一個請求消息和一個響應消息。就上述例子而言,用戶每請求一次那個web頁面,就產生11個TCP連接。
2、持久連接
非持久連接有一個很大的缺點就是,每一個http請求都需要建立一個TCP連接,就上面的例子而言,get一個html頁面就要建立十一次TCP連接,這是嚴重浪費資源行為!
首先,客戶得為每個待請求的對象建立並維護一個新的連接。對於每個這樣的連接,TCP得在客戶端和服務器端分配TCP緩沖區,並維持TCP變量。對於有可能同時為來自數百個不同客戶的請求提供服務的web服務器來說,這會嚴重增加其負擔。其次,如前所述,每個對象都有2個RTT的響應延長——一個RTT用於建立TCP連接,另—個RTT用於請求和接收對象。最后,每個對象都遭受TCP緩啟動,因為每個TCP連接都起始於緩啟動階段。不過並行TCP連接的使用能夠部分減輕RTT延遲和緩啟動延遲的影響。
【RTT(Round-Trip Time): 往返時延。在計算機網絡中它是一個重要的性能指標,表示從發送端發送數據開始,到發送端收到來自接收端的確認(接收端收到數據后便立即發送確認),總共經歷的時延。】
持久連接就能夠很好解決這一缺點,在持久連接情況下,服務器在發出響應后讓TCP連接繼續打開着。同一對客戶/服務器之間的后續請求和響應可以通過這個連接發送。整個Web頁面(上例中為包含一個基本HTMLL文件和10個圖像的頁面)自不用說可以通過單個持久TCP連接發送:甚至存放在同一個服務器中的多個web頁面也可以通過單個持久TCP連接發送。
通常,HTTP服務器在某個連接閑置一段特定時間后關閉它,而這段時間通常是可以配置的。
持久連接分為不帶流水線(without pipelining)和帶流水線(with pipelining)兩個版本。
不帶流水線的版本:
客戶只在收到前一個請求的響應后才發出新的請求。這種情況下,web頁面所引用的每個對象(上例中的10個圖像)都經歷1個RTT的延遲,用於請求和接收該對象。與非持久連接2個RTT的延遲相比,不帶流水線的持久連接已有所改善,不過帶流水線的持久連接還能進一步降低響應延遲。不帶流水線版本的另一個缺點是,服務器送出一個對象后開始等待下一個請求,而這個新請求卻不能馬上到達。這段時間服務器資源便閑置了。
帶流水線的持久連接:
HTTP/1.1的默認模式使用帶流水線的持久連接。這種情況下,HTTP客戶每碰到一個引用就立即發出一個請求,因而HTTP客戶可以一個接一個緊挨着發出各個引用對象的請求。服務器收到這些請求后,也可以一個接一個緊挨着發出各個對象。如果所有的請求和響應都是緊挨着發送的,那么所有引用到的對象一共只經歷1個RTT的延遲(而不是像不帶流水線的版本那樣,每個引用到的對象都各有1個RTT的延遲)。另外,帶流水線的持久連接中服務器空等請求的時間比較少。與非持久連接相比,持久連接(不論是否帶流水線)除降低了1個RTT的響應延遲外,緩啟動延遲也比較小。其原因在於既然各個對象使用同一個TCP連接,服務器發出第一個對象后就不必再以一開始的緩慢速率發送后續對象。相反,服務器可以按照第一個對象發送完畢時的速率開始發送下一個對象。
5.4、請求行
正如上面所講的,請求行以一個方法符號開頭,空格之后,一個請求URI,再空格,然后一個HTTP版本,最后一個回車換行。
它的作用是用來說明當前請求的最基本信息。
5.5、請求頭
(注:在HTTP/1.1 協議中,所有的請求頭,除Host外,都是可選的)
#請求頭的書寫形式為:Host:coblogs.com \r\n【標識符:內容 換行】
常見的請求頭:
1、Host:(發送請求時,該頭域是必需的)主要用於指定被請求資源的Internet主機和端口號,它通常從HTTP URL中提取出來的。HTTP/1.1請求必須包含主機頭域,否則系統會以400狀態碼返回。
例如: 我們在瀏覽器中輸入:http://www.guet.edu.cn/index.html,瀏覽器發送的請求消息中,就會包含Host請求頭域:Host:http://www.guet.edu.cn,此處使用缺省端口號80,若指定了端口號,則變成:Host:指定端口號。
2、User-Agent:告訴HTTP服務器,客戶端使用的操作系統和瀏覽器的名稱和版本。
例如: User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:53.0) Gecko/20100101 Firefox/53.0
3、Content-Type:例如:Content-Type: application/x-www-form-urlencoded
4、Accept-Language:瀏覽器申明自己接收的語言。語言跟字符集的區別:中文是語言,中文有多種字符集,比如big5,gb2312,gbk等等;例如:Accept-Language: en-us。如果請求消息中沒有設置這個報頭域,服務器假定客戶端對各種語言都可以接受。
5、Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
6、Accept-Encoding:瀏覽器申明自己可接收的編碼方法,通常指定壓縮方法,是否支持壓縮,支持什么壓縮方法(gzip,deflate);Servlet能夠向支持gzip的瀏覽器返回經gzip編碼的HTML頁面。許多情形下這可以減少5到10倍的下載時間。
例如: Accept-Encoding: gzip, deflate。如果請求消息中沒有設置這個域,服務器假定客戶端對各種內容編碼都可以接受。
7、Cookie:最重要的請求頭之一, 將cookie的值發送給HTTP服務器。
8、Connection:HTTP 1.1默認進行持久連接keep-alive。
例如:Connection: keep-alive 當一個網頁打開完成后,客戶端和服務器之間用於傳輸HTTP數據的TCP連接不會關閉,如果客戶端再次訪問這個服務器上的網頁,會繼續使用這一條已經建立的連接。
利用持久連接的優點,當頁面包含多個元素時(例如Applet,圖片),顯著地減少下載所需要的時間。要實現這一點,Servlet需要在應答中發送一個Content-Length頭,最簡單的實現方法是:先把內容寫入ByteArrayOutputStream,然后在正式寫出內容之前計算它的大小。
Connection: close 代表一個Request完成后,客戶端和服務器之間用於傳輸HTTP數據的TCP連接會關閉,當客戶端再次發送Request,需要重新建立TCP連接。
9、Keep-Alive:30保持持久連接30s
10、If-Modified-Since:把瀏覽器端緩存頁面的最后修改時間發送到服務器去,服務器會把這個時間與服務器上實際文件的最后修改時間進行對比。如果時間一致,那么返回304,客戶端就直接使用本地緩存文件。如果時間不一致,就會返回200和新的文件內容。客戶端接到之后,會丟棄舊文件,把新文件緩存起來,並顯示在瀏覽器中。
例如:If-Modified-Since: Sat, 06 May 2017 12:05:41 GMT
11、If-None-Match:If-None-Match和ETag一起工作,工作原理是在HTTP Response中添加ETag信息。 當用戶再次請求該資源時,將在HTTP Request 中加入If-None-Match信息(ETag的值)。如果服務器驗證資源的ETag沒有改變(該資源沒有更新),將返回一個304狀態告訴客戶端使用本地緩存文件。否則將返回200狀態和新的資源和Etag. 使用這樣的機制將提高網站的性能。
例如: If-None-Match: "03f2b33c0bfcc1:0"。
12、Pragma:指定“no-cache”值表示服務器必須返回一個刷新后的文檔,即使它是代理服務器而且已經有了頁面的本地拷貝;在HTTP/1.1版本中,它和Cache-Control:no-cache作用一模一樣。Pargma只有一個用法, 例如: Pragma: no-cache
13、Cache-Control:指定請求和響應遵循的緩存機制。緩存指令是單向的(響應中出現的緩存指令在請求中未必會出現),且是獨立的(在請求消息或響應消息中設置Cache-Control並不會修改另一個消息處理過程中的緩存處理過程)。請求時的緩存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached,響應消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age、s-maxage。
注意: 在HTTP/1.0版本中,只實現了Pragema:no-cache, 沒有實現Cache-Control
Cache-Control:Public 可以被任何緩存所緩存
Cache-Control:Private 內容只緩存到私有緩存中
Cache-Control:no-cache 所有內容都不會被緩存
Cache-Control:no-store 用於防止重要的信息被無意的發布。在請求消息中發送將使得請求和響應消息都不使用緩存。
Cache-Control:max-age 指示客戶機可以接收生存期不大於指定時間(以秒為單位)的響應。
Cache-Control:min-fresh 指示客戶機可以接收響應時間小於當前時間加上指定時間的響應。
Cache-Control:max-stale 指示客戶機可以接收超出超時期間的響應消息。如果指定max-stale消息的值,那么客戶機可以接收超出超時期指定值之內的響應消息。
14、Accept-Charset:瀏覽器可接受的字符集。如果在請求消息中沒有設置這個域,缺省表示任何字符集都可以接受。
15、Referer:包含一個URL,用戶從該URL代表的頁面出發訪問當前請求的頁面。提供了Request的上下文信息的服務器,告訴服務器我是從哪個鏈接過來的,比如從我主頁上鏈接到一個朋友那里,他的服務器就能夠從HTTP Referer中統計出每天有多少用戶點擊我主頁上的鏈接訪問他的網站。
例如: Referer:http://translate.google.cn/?hl=zh-cn&tab=wT
16、Content-Length:表示請求消息正文的長度。例如:Content-Length: 38。
17、From:請求發送者的email地址,由一些特殊的Web客戶程序使用,瀏覽器不會用到它。
18、Range:可以請求實體的一個或者多個子范圍。
例如:
表示頭500個字節:bytes=0-499
表示第二個500字節:bytes=500-999
表示最后500個字節:bytes=-500
表示500字節以后的范圍:bytes=500-
第一個和最后一個字節:bytes=0-0,-1
同時指定幾個范圍:bytes=500-600,601-999
但是服務器可以忽略此請求頭,如果無條件GET包含Range請求頭,響應會以狀態碼206(PartialContent)返回而不是以200(OK)。
5.6、請求主體
請求的主要用戶數據,就是POST數據。
如果方式為POST,則需要請求主體部分;GET則沒有請求主體
數據形式:類似name=XXX&pwd=XXXX的內容
6、HTTP響應
6.1、Response 消息的結構
響應消息的結構由三部分組成,響應行、相應頭、相應主體(即:狀態行、消息報頭、響應正文。)
【響 應 行】HTTP版本 空格 狀態碼 空格 狀態碼的文本描述 空格 CRLF(換行符)
【響 應 頭】:內容 CRLF(換行符)
【空 一 行】(表示響應頭結束)
【響應主體】所謂響應主體,就是服務器返回的資源的內容。即整個HTML文件。
6.2、響應行
響應數據的第一行,響應結果的概述。
狀態碼:
狀態代碼有3位數字組成,狀態描述給出了狀態代碼簡短的描述。狀態碼第一個數字定義了響應的類別,有五種可能取值:
1xx : 指示信息--表示請求已接收,繼續處理
2xx : 成功--表示請求已被成功接收、理解、接受
3xx : 重定向--要完成請求必須進行更進一步的操作
4xx : 客戶端錯誤--請求有語法錯誤或請求無法實現
5xx : 服務器端錯誤--服務器未能實現合法的請求
所有狀態碼如下(已折疊):

100——客戶必須繼續發出請求 101——客戶要求服務器根據請求轉換HTTP協議版本 200——交易成功 201——提示知道新文件的URL 202——接受和處理、但處理未完成 203——返回信息不確定或不完整 204——請求收到,但返回信息為空 205——服務器完成了請求,用戶代理必須復位當前已經瀏覽過的文件 206——服務器已經完成了部分用戶的GET請求 300——請求的資源可在多處得到 301——刪除請求數據 302——在其他地址發現了請求數據 303——建議客戶訪問其他URL或訪問方式 304——客戶端已經執行了GET,但文件未變化 305——請求的資源必須從服務器指定的地址得到 306——前一版本HTTP中使用的代碼,現行版本中不再使用 307——申明請求的資源臨時性刪除 400——錯誤請求,如語法錯誤 401——請求授權失敗 402——保留有效ChargeTo頭響應 403——請求不允許 404——沒有發現文件、查詢或URl 405——用戶在Request-Line字段定義的方法不允許 406——根據用戶發送的Accept拖,請求資源不可訪問 407——類似401,用戶必須首先在代理服務器上得到授權 408——客戶端沒有在用戶指定的餓時間內完成請求 409——對當前資源狀態,請求不能完成 410——服務器上不再有此資源且無進一步的參考地址 411——服務器拒絕用戶定義的Content-Length屬性請求 412——一個或多個請求頭字段在當前請求中錯誤 413——請求的資源大於服務器允許的大小 414——請求的資源URL長於服務器允許的長度 415——請求資源不支持請求項目格式 416——請求中包含Range請求頭字段,在當前請求資源范圍內沒有range指示值,請求也不包含If-Range請求頭字段 417——服務器不滿足請求Expect頭字段指定的期望值,如果是代理服務器,可能是下一級服務器不能滿足請求 500——服務器產生內部錯誤 501——服務器不支持請求的函數 502——服務器暫時不可用,有時是為了防止發生系統過載 503——服務器過載或暫停維修 504——關口過載,服務器使用另一個關口或服務來響應用戶,等待時間設定值較長 505——服務器不支持或拒絕支請求頭中指定的HTTP版本
6.3、響應頭
同理,請求頭!
HTTP常見的響應頭:
1、Date:表示消息發送的時間,時間的描述格式由rfc822定義。例如,Date:Sat, 06 May 2017 12:16:56 GMT。Date描述的時間表示世界標准時,換算成本地時間,需要知道用戶所在的時區。你可以用setDateHeader來設置這個頭以避免轉換時間格式的麻煩
2、Content-Type:WEB服務器告訴瀏覽器自己響應的對象的類型和字符集。Servlet默認為text/plain,但通常需要顯式地指定為text/html。由於經常要設置Content-Type,因此HttpServletResponse提供了一個專用的方法setContentType。可在web.xml文件中配置擴展名和MIME類型的對應關系。
例如:
Content-Type: text/html;charset=utf-8
Content-Type:text/html;charset=GB2312
Content-Type: image/jpeg
媒體類型的格式為:大類/小類,比如text/html。
IANA(The Internet Assigned Numbers Authority,互聯網數字分配機構)定義了8個大類的媒體類型,分別是:
application— (比如: application/vnd.ms-excel.)
audio (比如: audio/mpeg.)
image (比如: image/png.)
message (比如,:message/http.)
model(比如:model/vrml.)
multipart (比如:multipart/form-data.)
text(比如:text/html.)
video(比如:video/quicktime.)
3、Expires:指明應該在什么時候認為文檔已經過期,從而不再緩存它,重新從服務器獲取,會更新緩存。過期之前使用本地緩存。HTTP1.1的客戶端和緩存會將非法的日期格式(包括0)看作已經過期。
eg:為了讓瀏覽器不要緩存頁面,我們也可以將Expires實體報頭域,設置為0。
例如: Expires: Tue, 08 Feb 2022 11:35:14 GMT
4、P3P:用於跨域設置Cookie, 這樣可以解決iframe跨域訪問cookie的問題
例如: P3P: CP=CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR
5、Set-Cookie:非常重要的header, 用於把cookie發送到客戶端瀏覽器,每一個寫入cookie都會生成一個Set-Cookie。
例如: Set-Cookie: sc=4c31523a; path=/; domain=.acookie.taobao.com
6、ETag:和If-None-Match 配合使用。
7、Last-Modified:用於指示資源的最后修改日期和時間。Last-Modified也可用setDateHeader方法來設置。
8、Content-Range:用於指定整個實體中的一部分的插入位置,他也指示了整個實體的長度。在服務器向客戶返回一個部分響應,它必須描述響應覆蓋的范圍和整個實體長度。一般格式:Content-Range:bytes-unitSPfirst-byte-pos-last-byte-pos/entity-length。
例如,傳送頭500個字節次字段的形式:Content-Range:bytes0-499/1234如果一個http消息包含此節(例如,對范圍請求的響 應或對一系列范圍的重疊請求),Content-Range表示傳送的范圍。
9、Content-Length:指明實體正文的長度,以字節方式存儲的十進制數字來表示。在數據下行的過程中,Content-Length的方式要預先在服務器中緩存所有數據,然后所有數據再一股腦兒地發給客戶端。只有當瀏覽器使用持久HTTP連接時才需要這個數據。如果你想要利用持久連接的優勢,可以把輸出文檔寫入ByteArrayOutputStram,完成后查看其大小,然后把該值放入Content-Length頭,最后通過byteArrayStream.writeTo(response.getOutputStream()發送內容。
例如: Content-Length: 19847
10、Content-Encoding:WEB服務器表明自己使用了什么壓縮方法(gzip,deflate)壓縮響應中的對象。只有在解碼之后才可以得到Content-Type頭指定的內容類型。利用gzip壓縮文檔能夠顯著地減少HTML文檔的下載時間。Java的GZIPOutputStream可以很方便地進行gzip壓縮,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet應該通過查看Accept-Encoding頭(即request.getHeader("Accept-Encoding"))檢查瀏覽器是否支持gzip,為支持gzip的瀏覽器返回經gzip壓縮的HTML頁面,為其他瀏覽器返回普通頁面。
例如:Content-Encoding:gzip
11、Content-Language:WEB服務器告訴瀏覽器自己響應的對象所用的自然語言。
例如: Content-Language:da。沒有設置該域則認為實體內容將提供給所有的語言閱讀。
12、Server:指明HTTP服務器用來處理請求的軟件信息。例如:Server: Microsoft-IIS/7.5、Server:Apache-Coyote/1.1。此域能包含多個產品標識和注釋,產品標識一般按照重要性排序
13、X-AspNet-Version:如果網站是用ASP.NET開發的,這個header用來表示ASP.NET的版本。
例如: X-AspNet-Version: 4.0.30319
14、X-Powered-By:表示網站是用什么技術開發的。
例如: X-Powered-By: ASP.NET
15、Connection:keep-alive /close
16、Location:用於重定向一個新的位置,包含新的URL地址。表示客戶應當到哪里去提取文檔。Location通常不是直接設置的,而是通過HttpServletResponse的sendRedirect方法,該方法同時設置狀態代碼為302。Location響應報頭域常用在更換域名的時候。
17、Refresh:表示瀏覽器應該在多少時間之后刷新文檔,以秒計。除了刷新當前文檔之外,你還可以通過setHeader("Refresh", "5; URL=http://host/path")讓瀏覽器讀取指定的頁面。注意這種功能通常是通過設置HTML頁面HEAD區的<META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path">實現,這是因為,自動刷新或重定向對於那些不能使用CGI或Servlet的HTML編寫者十分重要。但是,對於Servlet來說,直接設置Refresh頭更加方便。注意Refresh的意義是“N秒之后刷新本頁面或訪問指定頁面”,而不是“每隔N秒刷新本頁面或訪問指定頁面”。因此,連續刷新要求每次都發送一個Refresh頭,而發送204狀態代碼則可以阻止瀏覽器繼續刷新,不管是使用Refresh頭還是<META HTTP-EQUIV="Refresh" ...>。注意Refresh頭不屬於HTTP 1.1正式規范的一部分,而是一個擴展,但Netscape和IE都支持它。
6.4、響應主體
就是服務器返回的資源的內容。即整個HTML文件
7、HTTP請求詳細過程
從前面講解中我們大概對HTTP有了一個基本的認識,那么接下來我們就詳細研究了解HTTP請求的具體過程。
引用咸魚老弟的博客文章
7.1、 輸入地址
當我們開始在瀏覽器中輸入網址的時候,瀏覽器其實就已經在智能的匹配可能得 url 了,他會從歷史記錄,書簽等地方,找到已經輸入的字符串可能對應的 url,然后給出智能提示,讓你可以補全url地址。對於 google的chrome 的瀏覽器,他甚至會直接從緩存中把網頁展示出來,就是說,你還沒有按下 enter,頁面就出來了。
7.2、瀏覽器查找域名的IP
1、請求一旦發起,瀏覽器首先要做的事情就是解析這個域名,一般來說,瀏覽器會首先查看本地硬盤的 hosts 文件,看看其中有沒有和這個域名對應的規則,如果有的話就直接使用 hosts 文件里面的 ip 地址。
2、如果在本地的 hosts 文件沒有能夠找到對應的 ip 地址,瀏覽器會發出一個 DNS請求到本地DNS服務器 。本地DNS服務器一般都是你的網絡接入服務器商提供,比如中國電信,中國移動。
3、查詢你輸入的網址的DNS請求到達本地DNS服務器之后,本地DNS服務器會首先查詢它的緩存記錄,如果緩存中有此條記錄,就可以直接返回結果,此過程是遞歸的方式進行查詢。如果沒有,本地DNS服務器還要向DNS根服務器進行查詢。
4、根DNS服務器沒有記錄具體的域名和IP地址的對應關系,而是告訴本地DNS服務器,你可以到域服務器上去繼續查詢,並給出域服務器的地址。這種過程是迭代的過程。
5、本地DNS服務器繼續向域服務器發出請求,在這個例子中,請求的對象是.com域服務器。.com域服務器收到請求之后,也不會直接返回域名和IP地址的對應關系,而是告訴本地DNS服務器,你的域名的解析服務器的地址。
6、最后,本地DNS服務器向域名的解析服務器發出請求,這時就能收到一個域名和IP地址對應關系,本地DNS服務器不僅要把IP地址返回給用戶電腦,還要把這個對應關系保存在緩存中,以備下次別的用戶查詢時,可以直接返回結果,加快網絡訪問。
下面這張圖很完美的解釋了這一過程:
知識擴展:
1)什么是DNS?
DNS(Domain Name System,域名系統),因特網上作為域名和IP地址相互映射的一個分布式數據庫,能夠使用戶更方便的訪問互聯網,而不用去記住能夠被機器直接讀取的IP數串。通過主機名,最終得到該主機名對應的IP地址的過程叫做域名解析(或主機名解析)。
通俗的講,我們更習慣於記住一個網站的名字,比如www.baidu.com,而不是記住它的ip地址,比如:167.23.10.2。而計算機更擅長記住網站的ip地址,而不是像www.baidu.com等鏈接。因為,DNS就相當於一個電話本,比如你要找www.baidu.com這個域名,那我翻一翻我的電話本,我就知道,哦,它的電話(ip)是167.23.10.2。
2)DNS查詢的兩種方式:遞歸查詢和迭代查詢
1、遞歸解析
當局部DNS服務器自己不能回答客戶機的DNS查詢時,它就需要向其他DNS服務器進行查詢。此時有兩種方式,如圖所示的是遞歸方式。局部DNS服務器自己負責向其他DNS服務器進行查詢,一般是先向該域名的根域服務器查詢,再由根域名服務器一級級向下查詢。最后得到的查詢結果返回給局部DNS服務器,再由局部DNS服務器返回給客戶端。
簡單來講,就是參與此次尋找IP的所有服務器,最后都能夠得到該域名對應的IP信息(將信息進行往返傳送!)
2、迭代解析
當局部DNS服務器自己不能回答客戶機的DNS查詢時,也可以通過迭代查詢的方式進行解析,如圖所示。局部DNS服務器不是自己向其他DNS服務器進行查詢,而是把能解析該域名的其他DNS服務器的IP地址返回給客戶端DNS程序,客戶端DNS程序再繼續向這些DNS服務器進行查詢,直到得到查詢結果為止。也就是說,迭代解析只是幫你找到相關的服務器而已,而不會幫你去查。比如說:baidu.com的服務器ip地址在192.168.4.5這里,你自己去查吧,本人比較忙,只能幫你到這里了。
簡單的來講,就是只有最后一台服務器與最初的服務器進行該域名/IP信息的傳送!
3)DNS域名稱空間的組織方式
我們在前面有說到根DNS服務器,域DNS服務器,這些都是DNS域名稱空間的組織方式。按其功能命名空間中用來描述 DNS 域名稱的五個類別的介紹詳見下表中,以及與每個名稱類型的示例
4)DNS負載均衡
當一個網站有足夠多的用戶的時候,假如每次請求的資源都位於同一台機器上面,那么這台機器隨時可能會蹦掉。處理辦法就是用DNS負載均衡技術,它的原理是在DNS服務器中為同一個主機名配置多個IP地址,在應答DNS查詢時,DNS服務器對每個查詢將以DNS文件中主機記錄的IP地址按順序返回不同的解析結果,將客戶端的訪問引導到不同的機器上去,使得不同的客戶端訪問不同的服務器,從而達到負載均衡的目的。例如可以根據每台機器的負載量,該機器離用戶地理位置的距離等等。
7.3、瀏覽器攜帶IP地址向Web服務器發起HTTP請求
拿到域名對應的IP地址之后,瀏覽器會以一個隨機端口(1024<端口<65535)向服務器的WEB程序(常用的有httpd,nginx等)80端口發起TCP的連接請求。
這個連接請求到達服務器端后(這中間通過各種路由設備,局域網內除外),進入到網卡,然后是進入到內核的TCP/IP協議棧(用於識別該連接請求,解封包,一層一層的剝開),還有可能要經過Netfilter防火牆(屬於內核的模塊)的過濾,最終到達WEB程序,最終建立了TCP/IP的連接。
TCP連接參考上面
建立了TCP連接之后,發起一個http請求。一個典型的 http request header 一般需要包括請求的方法,例如 GET 或者 POST 等,不常用的還有 PUT 和 DELETE 、HEAD、OPTION以及 TRACE 方法,一般的瀏覽器只能發起 GET 或者 POST 請求。
7.4、服務器的永久重定向響應
服務器給瀏覽器響應一個301永久重定向響應,這樣瀏覽器就會訪問“http://www.google.com/” 而非“http://google.com/”。
為什么服務器一定要重定向而不是直接發送用戶想看的網頁內容呢?其中一個原因跟搜索引擎排名有關。如果一個頁面有兩個地址,就像http://www.yy.com/和http://yy.com/,搜索引擎會認為它們是兩個網站,結果造成每個搜索鏈接都減少從而降低排名。而搜索引擎知道301永久重定向是什么意思,這樣就會把訪問帶www的和不帶www的地址歸到同一個網站排名下。還有就是用不同的地址會造成緩存友好性變差,當一個頁面有好幾個名字時,它可能會在緩存里出現好幾次。
擴展知識
1)301和302的區別。
301和302狀態碼都表示重定向,就是說瀏覽器在拿到服務器返回的這個狀態碼后會自動跳轉到一個新的URL地址,這個地址可以從響應的Location首部中獲取(用戶看到的效果就是他輸入的地址A瞬間變成了另一個地址B)——這是它們的共同點。
他們的不同在於。301表示舊地址A的資源已經被永久地移除了(這個資源不可訪問了),搜索引擎在抓取新內容的同時也將舊的網址交換為重定向之后的網址;
302表示舊地址A的資源還在(仍然可以訪問),這個重定向只是臨時地從舊地址A跳轉到地址B,搜索引擎會抓取新的內容而保存舊的網址。 SEO302好於301
2)重定向原因:
3)什么時候進行301或者302跳轉呢?
7.5、發出新的請求(重定向)
現在瀏覽器知道了 "http://www.google.com/"才是要訪問的正確地址,所以它會發送另一個http請求。重復上面的http請求步驟
7.6、服務器主機處理
經過前面的重重步驟,我們終於將我們的http請求發送到了服務器這里,其實前面的重定向已經是到達服務器了,那么,服務器是如何處理我們的請求的呢?
后端從在固定的端口接收到TCP報文開始,它會對TCP連接進行處理,對HTTP協議進行解析,並按照報文格式進一步封裝成HTTP Request對象,供上層使用。
【一些大一點的網站會將你的請求到反向代理服務器中,因為當網站訪問量非常大,網站越來越慢,一台服務器已經不夠用了。於是將同一個應用部署在多台服務器上,將大量用戶的請求分配給多台機器處理。此時,客戶端不是直接通過HTTP協議訪問某網站應用服務器,而是先請求到Nginx,Nginx再請求應用服務器,然后將結果返回給客戶端,這里Nginx的作用是反向代理服務器。同時也帶來了一個好處,其中一台服務器萬一掛了,只要還有其他服務器正常運行,就不會影響用戶使用。】
7.7、Web應用服務器處理http請求
【假設服務器端使用nginx+php(fastcgi)架構提供服務】
假設我此時輸入的URL為http://www.mecnblogs.com/
① nginx讀取配置文件,並尋找文件
當服務器主機將瀏覽器發送過來的所有數據通過各個網絡層的相應協議的規定進行了解密以及封裝,最后將數據包送達應用層使用。(可參考TCP/IP網絡模型)
當Nginx在收到瀏覽器 GET / 請求時,會讀取http請求里面的頭部信息,根據Host來匹配 自己的所有的虛擬主機的配置文件的server_name,看看有沒有匹配的,有匹配那么就讀取該虛擬主機的配置,發現如下配置:
root /web/echo
通過這個就知道所有網頁文件的就在這個目錄下 這個目錄就是/ 當我們http://www.mecnblogs.com/時就是訪問這個目錄下面的文件,例如訪問http://www.mecnblogs.com/index.html,那么代表/web/echo下面有個文件叫index.html
index index.html index.htm index.php
通過這個就能得知網站的首頁文件是那個文件,也就是我們在入http://www.mecnblogs.com/ ,nginx就會自動幫我們把index.html(假設首頁是index.php 當然是會嘗試的去找到該文件,如果沒有找到該文件就依次往下找,如果這3個文件都沒有找到,那么就拋出一個404錯誤)加到后面,那么添加之后的URL是/index.php,然后根據后面的配置進行處理
location ~ .*\.php(\/.*)*$ { root /web/echo; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; astcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }
這一段配置指明凡是請求的URL中匹配(這里是啟用了正則表達式進行匹配) *.php后綴的(后面跟的參數)都交給后端的fastcgi進程進行處理。
② 把php文件交給fastcgi進程去處理
於是nginx把/index.php這個URL交給了后端的fastcgi進程處理,等待fastcgi處理完成后(結合數據庫查詢出數據,填充模板生成html文件)返回給nginx一個index.html文檔,Nginx再把這個index.html返回給瀏覽器(通過HTTP協議返回,即HTTP響應【響應消息結構可以參考上面】),於是乎瀏覽器就拿到了首頁的html代碼,同時nginx寫一條訪問日志到日志文件中去。
【擴展:】
nginx是怎么找index.php文件的?
當nginx發現需要/web/echo/index.php文件時,就會向內核發起IO系統調用(因為要跟硬件打交道,這里的硬件是指硬盤,通常需要靠內核來操作,而內核提供的這些功能是通過系統調用來實現的),告訴內核,我需要這個文件,內核從/開始找到web目錄,再在web目錄下找到echo目錄,最后在echo目錄下找到index.php文件,於是把這個index.php從硬盤上讀取到內核自身的內存空間,然后再把這個文件復制到nginx進程所在的內存空間,於是乎nginx就得到了自己想要的文件了。
尋找文件在文件系統層面是怎么操作的?
比如nginx需要得到/web/echo/index.php這個文件
每個分區(像ext3 ext3等文件系統,block塊是文件存儲的最小單元 默認是4096字節)都是包含元數據區和數據區,每一個文件在元數據區都有元數據條目(一般是128字節大小),每一個條目都有一個編號,我們稱之為inode(index node 索引節點),這個inode里面包含 文件類型、權限、連接次數、屬主和數組的ID、時間戳、這個文件占據了那些磁盤塊也就是塊的編號(block,每個文件可以占用多個block,並且block不一定是連續的,每個block是有編號的),如下圖所示:
還有一個要點:目錄其實也普通是文件,也需要占用磁盤塊,目錄不是一個容器。你看默認創建的目錄就是4096字節,也就說只需要占用一個磁盤塊,但這是不確定的。所以要找到目錄也是需要到元數據區里面找到對應的條目,只有找到對應的inode就可找到目錄所占用的磁盤塊。
那到底目錄里面存放着什么,難道不是文件或者其他目錄嗎?
其實目錄存着這么一張表(姑且這么理解),里面放着 目錄或者文件的名稱和對應的inode號(暫時稱之為映射表),如下圖:
假設
/ 在數據區占據 1、2號block ,/其實也是一個目錄 里面有3個目錄 web 111
web 占據 5號block 是目錄 里面有2個目錄 echo data
echo 占據 11號 block 是目錄 里面有1個文件 index.php
index.php 占據 15 16號 block 是文件
其在文件系統中分布如下圖所示:
那么內核究竟是怎么找到index.php這個文件的呢?
內核拿到nginx的IO系統調用要獲取/web/echo/index.php這個文件請求之后
① 內核讀取元數據區 / 的inode,從inode里面讀取/所對應的數據塊的編號,然后在數據區找到其對應的塊(1 2號塊),讀取1號塊上的映射表找到web這個名稱在元數據區對應的inode號
② 內核讀取web對應的inode(3號),從中得知web在數據區對應的塊是5號塊,於是到數據區找到5號塊,從中讀取映射表,知道echo對應的inode是5號,於是到元數據區找到5號inode
③ 內核讀取5號inode,得到echo在數據區對應的是11號塊,於是到數據區讀取11號塊得到映射表,得到index.php對應的inode是9號
④ 內核到元數據區讀取9號inode,得到index.php對應的是15和16號數據塊,於是就到數據區域找到15 16號塊,讀取其中的內容,得到index.php的完整內容
7.8、瀏覽器處理並顯示html文件
在瀏覽器沒有完整接受全部HTML文檔時,它就已經開始顯示這個頁面了,瀏覽器是如何把頁面呈現在屏幕上的呢?不同瀏覽器可能解析的過程不太一樣,這里我們只介紹webkit的渲染過程,下圖對應的就是WebKit渲染的過程,這個過程包括:
解析html以構建dom樹 -> 構建render樹 -> 布局render樹 -> 繪制render樹
在瀏覽器顯示的時候,當遇到要獲取外圖片,CSS,JS文件等等時,瀏覽器將會發起不斷發起異步的http請求來獲取這些資源。
8、總結
站在巨人的肩膀上來學習確實能夠讓自己的眼界更加開闊,同時深入學習與鞏固HTTP這方面的知識,能夠讓自己深入了解Web的B/S結構、Web通訊的具體過程,有助於自己日后的Web開發。同時也為接下來的面試做准備。在此當然是要感謝各位前輩大牛啦。
9、參考文獻
1. 《圖解TCP-IP協議》
2. 《一次完整的HTTP事務是怎樣一個過程?》
3. 《【原】老生常談-從輸入url到頁面展示到底發生了什么》
4. 《淺析HTTP協議》
5. 《HTTP協議詳解》
(以上是自己的一些見解,若有不足或者錯誤的地方請各位指出)
作者:那一葉隨風 http://www.cnblogs.com/phpstudy2015-6/
原文地址:http://www.cnblogs.com/phpstudy2015-6/p/6810130.html
聲明:只代表本人在工作學習中某一時間內總結的觀點或結論。轉載時請在文章頁面明顯位置給出原文鏈接