第二天 Http流程Get和Post,長連接和短連接


Tcp/Http請求流程
1:首先明確一下Tcp層是沒有“請求”這一說法的,Tcp是一種通信的方式,“請求”一詞是事務上的概念,HTTP是一種事務協議,所以說,HTTP請求這種說法沒錯,但是Tcp請求是錯誤的,
2:TCP/IP協議的三次握手和四次分手,用一張網絡上的圖來說明上面這個圖是網絡上面長說的,下面我們使用wireshark來具體看下 TCP/IP請求wireshark:抓包明顯看到TCP的多次握手流程

 

 

客戶端進行一次HTTP請求具體流程如下:

 

 

1:客戶端發送一次握手SYN到服務端,
2:這個時候服務端接收到客戶端的請求,把請求放入到SYN隊列里面,並且向客戶端返回SYN+ack j+1信息
3:客戶端收到服務端的返回信息再次確認,然后再次發送請求到服務端,ack k+1,這個時候服務端吧SYN隊列里面的請求取出來,然后插入到ACCEPT隊列中,
上面三次操作是服務端系統做的事,具體請求還沒有到我們自己的程序,只有到了ACCEPT隊列,然后我們程序服務器比如iis,或者tomcat去處理ACCEPT請求才能吧請求轉入到我們程序
比如我們NIO編程,服務端開始程序就是循環監控ACCEPT如果有確認好的請求(三次握手成功的)就拿出來,轉到我們程序處理

 

 

而且通過wireshark抓包我們看到不管是請三次握手還是后面的四次分手,都是TCP方式,只有建立成功連接發送請求數據的時候是HTTP請求,TCP報文承載了HTTP協議的請求(Request)和響應(Response)
Get和Post的區別
首先我們先看一下HTTP協議
Http請求的請求協議:分為三部分,
第一部分是請求行,主要是請求方式,請求url和請求參數,用空格分隔,當讀取到回車(\r\n)的時候說明請求行讀取完成
第二部分是請求頭:是鍵值對,主要是一些定制的頭信息,cookie和自己設置的信息,用:分隔,當讀取到回車(\r\n)的時候說明請求行讀取完成
第三部分是請求體:這個主要是我們請求的數據,具體數據怎么操作在請求頭上面有說明Content-type

 

 

Http響應的響應協議
 

 

 

通過我們上面的TCP協議和HTTP協議我們可以看到,Get和Post請求本質是一樣的,都需要三次握手和四次分手,而且都是Http協議,所以協議信息也是一樣的 ,
但是get和post具體的區別是什么那 ,還是發送協議的時候信息不一樣,我們知道一個get請求比如 http://xxx/api/go_userInfo/getuserinfolist?_appid=xxx&fields=userid,sex&useridlist=,40636794
這樣一個請求在請求協議的請求行的url里面就已經吧我們的請求數據一起發送了,所以不需要我們在發送請求體來發送我們請求數據,但是這樣做有也一定的確定因為url能承載的數據是有限的,一般瀏覽器承載是2k
服務器默認是64k
 

 

 

但是post不同,post請求的請求行的url是協議+虛擬目錄不包含我們具體的數據,所以他的數據是通過請求體來發送的,因為是通過請求體來發送數據所以可以發送很大的數據,而且可以發送很多次
(如果數據太大,一次發送不完,可以多次發送,都是在一個請求內)
 

 

 

所以說get請求只需要發送一次就可以完成數據的存取,但是post因為要發送請求體,所以可能需要一次以上的請求,不同瀏覽器的處理不同,有的瀏覽器post會發送兩個包,吧請求行和請求頭作為一次發送
請求體作為一次發送,但是有的瀏覽器值發送一次比如(火狐)
長連接
短連接:一次請求就釋放的連接【比較簡單,不用管理連接狀態,維護連接,因為當前連接都是可用的連接】
長連接:完成一期請求不釋放,保持這個鏈接,為后面的請求復用【比較復雜,需要維護連接的狀態,和鏈接心跳,】
下面我們再來說一下長連接【 TCP 本身並沒有長短連接的區別 ,長短與否,完全取決於我們怎么用它】,Http長連接和服務端長連接是不一樣的 ,從名字我們可以明白,所謂長連接,指在一個TCP連接上可以連續發送多個數據包,在TCP連接保持期間,如果沒有數據包發送,需要雙方發檢測包以維持此連,短連接是指通信雙方有數據交互時,就建立一個TCP連接,數據發送完成后,則斷開此TCP連接,Tcp連接只要兩個ip建立了連接那就是連接了沒有長短之分,並且連接的兩個ip都可以主動斷開連接,也都可以主動推送或者拉取數據,是全雙工的
但是Http協議規定,請求是半雙工的,就是只能有客戶端來主動獲取數據,主動斷開,
Http協議長連接:在1.1以后http協議已經默認開啟了長連接,但是這種長連接是為了連接的復用,畢竟我們一次http請求要三次握手和四次分手,這個是很耗費時間的 ,比如說我們有一個頁面,里面有一下幾個請求,http://www.xxx.com/aaa, http://www.xxx.com/bbb, http://www.xxx.com/ccc
當我們瀏覽器發送http請求的時候比如我們先請求了http://www.xxx.com/aa這個請求,如果不是http長連接 和如何,先三次握手然后發送Http請求http://www.xxx.com/aaa然后在四次分手,一次請求結束,然后在同樣的流程去請求下一個,這樣會很耗費時間和性能,
如果是http長連接又會怎么處理那:客戶端先三次握手,然后發送Http請求http://www.xxx.com/aaa請求結束后我們不會進行四次分手,而是吧這個連接保持着,然后在發送http://www.xxx.com/bbb和http://www.xxx.com/ccc,當這個鏈接保持一段時間沒有在發送任何請求的時候在
釋放掉,進行四次分手,這就是Http協議的長連接
 
Post請求Content-Type的四種方式【來源於網上博客,發現博主寫的很不錯,這里做下記錄】
Content-Type:是用來告訴服務端有什么方式來解析請求體,
1:Content-Type: application/x-www-form-urlencoded; charset=UTF-8最常見的方式,如果不設置 Content-Type:默認就是這個, application/x-www-form-urlencoded是使用 url編碼來解析請求體
# python腳本 import requests url = "http://httpbin.org/post" data = {"name":"西園公子","age":"666"} headers = {"Content-type":"application/x-www-form-urlencoded"} content = requests.post(url=url,data=data,).text print(content) # 網絡請求: POST http://httpbin.org/post HTTP/1.1 Host httpbin.org User-Agent python-requests/2.24.0 Accept-Encoding gzip, deflate Accept */* Content-Length 49 Content-Type application/x-www-form-urlencoded Connection keep-alive # 下面是表單內容 name=%E8%A5%BF%E5%9B%AD%E5%85%AC%E5%AD%90&age=666 # 可以看出漢字是使用utf8編碼的
2:multipart/form-data (鍵值對型數據)
這種方式是想服務端提交文件,或者請求數據中包含文件,客戶端會生成一個boundary 來做內容分割
import requests from requests_toolbelt import MultipartEncoder m = MultipartEncoder( fields={'field0': 'value1', 'field1': 'value2', 'field2': ('filename', open('data.txt', 'rb'), 'text/plain')} ) content = requests.post('http://httpbin.org/post', data=m, headers={'Content-Type': m.content_type}).text print(content) print(m.content_type) # 1、網絡請求: POST http://httpbin.org/post HTTP/1.1 Host: httpbin.org User-Agent: python-requests/2.24.0 Accept-Encoding: gzip, deflate Accept: */* Content-Type: multipart/form-data; boundary=e48c73a7a42e403d868095dc3d060962 Content-Length: 222 Connection: keep-alive # 下面是編碼的表單內容 --e48c73a7a42e403d868095dc3d060962 Content-Disposition: form-data; name="field0" value1 --e48c73a7a42e403d868095dc3d060962 Content-Disposition: form-data; name="field1" value2 --e48c73a7a42e403d868095dc3d060962-- Content-Disposition: form-data; name="field2"; filename="filename" Content-Type: text/plain ä½ å¥½ï¼Œè¥¿å›­å…¬å­ï½ž --25c88ddc918d40e7a3cd5be0d62476b7--
如上面Content-Type: multipart/form-data; boundary=e48c73a7a42e403d868095dc3d060962,其中e48c73a7a42e403d868095dc3d060962 是請求體的內容分隔符
分隔符后面跟着的是鍵值對的建的名稱,也就是我們的參數名 如:Content-Disposition: form-data; name="field0"中的field0
然后讀取到換行符,在緊跟着就是鍵值對的值了如value1,當讀取到下一個分隔符 ,或者數據讀取完了,表明當前數據的鍵值對數據讀取完整
3:application/json (Json 類型數據)
# 1、原生網絡請求 POST /post HTTP/1.1 Host: httpbin.org User-Agent: python-requests/2.24.0 Accept-Encoding: gzip, deflate Accept: */* Content-Type: application/json Content-Length: 62 Connection: keep-alive # 下面是編碼成json數據 的表單內容 "{\"name\": \"\\u516c\\u5b50\\u54e5\", \"hobby\": \"coding\"}"
 
4:text/xml (xml)
import requests # from requests_toolbelt import MultipartEncoder p_data = """ <?xml version="1.0"?> <methodCall> <methodName>examples.getStateName</methodName> <params> <param> <value><i4>41</i4></value> </param> </params> </methodCall> """ content = requests.post(url='http://httpbin.org/post',data=p_data,headers={'Content-Type':'text/xml'}).text print(content) # 2、打印數據 { "args": {}, "data": "\n<?xml version=\"1.0\"?>\n<methodCall>\n <methodName>examples.getStateName</methodName>\n <params>\n <param>\n <value><i4>41</i4></value>\n </param>\n </params>\n</methodCall>\n", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "200", "Content-Type": "text/xml", "Host": "httpbin.org", "User-Agent": "python-requests/2.24.0", "X-Amzn-Trace-Id": "Root=1-60024d51-775180ce108409b413dc68c6" }, "json": null, "origin": "106.33.40.219", "url": "http://httpbin.org/post" }
Http跨域
首先要說明的是HTTP協議本身是沒有跨域請求的設置的,跨域請求的限制是瀏覽器為了安全考慮加上去的【服務端同源策略】。
然后我們要了解什么算跨域 我們知道一個url分為 協議://域名:端口,這樣組成,如果不寫端口默認是80,這三部分之中任何一部分不一樣就會形成跨域
瀏覽器提供了兩種方式的跨域請求
1:瀏覽器不限制script標簽、img標簽、link標簽的跨域請求,所以我們可以使用js在頁面加載的時候 生成script標簽來做跨域請求 ,但是這種請求只能是get的,如下
var s = document.createElement('script'); var b = false; s.setAttribute('type', 'text/javascript'); s.setAttribute('src', args); s.id = "tempscript" + result;
2:CORS跨域資源共享(CORS)
在sever返回的Response Head中添加Access-Control-Allow-Origin
瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)只要同時滿足以下兩大條件,就屬於簡單請求。
簡單請求
(1) 請求方法是以下三種方法之一:
 
  • HEAD
  • GET
  • POST
 
(2)HTTP的頭信息不超出以下幾種字段:
  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
這是為了兼容表單(form),因為歷史上表單一直可以發出跨域請求。AJAX 的跨域設計就是,只要表單可以發,AJAX 就可以直接發。凡是不同時滿足上面兩個條件,就屬於非簡單請求。
瀏覽器對這兩種請求的處理,是不一樣的。
對於簡單請求:
瀏覽器直接發出CORS請求。具體來說,就是在頭信息之中,增加一個Origin字段來說明,本次請求來自哪個源(協議 + 域名 + 端口)。服務器根據這個值,決定是否同意這次請求。
如果Origin指定的源,不在許可范圍內,服務器會返回一個正常的HTTP回應。瀏覽器發現,這個回應的頭信息沒有包含Access-Control-Allow-Origin字段就知道出錯了,
從而拋出一個錯誤,被XMLHttpRequest的onerror回調函數捕獲。注意,這種錯誤無法通過狀態碼識別,因為HTTP回應的狀態碼有可能是200
Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin: 該字段是必須的。它的值要么是請求時Origin字段的值,要么是一個*,表示接受任意域名的請求
Access-Control-Allow-Credentials: 該字段可選。它的值是一個布爾值,表示是否允許發送Cookie。默認情況下,Cookie不包括在CORS請求之中。設為true,即表示服務器明確許可,Cookie可以包含在請求中,一起發給服務器。這個值也只能設為true,如果服務器不要瀏覽器發送Cookie,刪除該字段即可。
Access-Control-Expose-Headers: 該字段可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必須在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。
withCredentials:
CORS請求默認不發送Cookie和HTTP認證信息。如果要把Cookie發到服務器,一方面要服務器同意,指定Access-Control-Allow-Credentials=true字段。
另一方面,開發者必須在AJAX請求中打開withCredentials屬性。 var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
否則,即使服務器同意發送Cookie,瀏覽器也不會發送。或者,服務器要求設置Cookie,瀏覽器也不會處理。
但是,如果省略withCredentials設置,有的瀏覽器還是會一起發送Cookie。這時,可以顯式關閉
非簡單請求
4.1 預檢請求
非簡單請求是那種對服務器有特殊要求的請求,比如請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json。
非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。
瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可以使用哪些HTTP動詞和頭信息字段。只有得到肯定答復,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。
下面是一段瀏覽器的JavaScript腳本。
var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
上面代碼中,HTTP請求的方法是PUT,並且發送一個自定義頭信息X-Custom-Header。
瀏覽器發現,這是一個非簡單請求,就自動發出一個"預檢"請求,要求服務器確認可以這樣請求。下面是這個"預檢"請求的HTTP頭信息。
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
"預檢"請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。頭信息里面,關鍵字段是Origin,表示請求來自哪個源。
除了Origin字段,"預檢"請求的頭信息包括兩個特殊字段。
(1)Access-Control-Request-Method
該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,上例是PUT。
(2)Access-Control-Request-Headers
該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段,上例是X-Custom-Header。
4.2 預檢請求的回應
服務器收到"預檢"請求以后,檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,確認允許跨源請求,就可以做出回應。
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
上面的HTTP回應中,關鍵的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以請求數據。該字段也可以設為星號,表示同意任意跨源請求。
Access-Control-Allow-Origin: *
如果服務器否定了"預檢"請求,會返回一個正常的HTTP回應,但是沒有任何CORS相關的頭信息字段。這時,瀏覽器就會認定,服務器不同意預檢請求,因此觸發一個錯誤,被XMLHttpRequest對象的onerror回調函數捕獲。控制台會打印出如下的報錯信息。
XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
服務器回應的其他CORS相關字段如下。
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000
(1)Access-Control-Allow-Methods
該字段必需,它的值是逗號分隔的一個字符串,表明服務器支持的所有跨域請求的方法。注意,返回的是所有支持的方法,而不單是瀏覽器請求的那個方法。這是為了避免多次"預檢"請求。
(2)Access-Control-Allow-Headers
如果瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,表明服務器支持的所有頭信息字段,不限於瀏覽器在"預檢"中請求的字段。
(3)Access-Control-Allow-Credentials
該字段與簡單請求時的含義相同。
(4)Access-Control-Max-Age
該字段可選,用來指定本次預檢請求的有效期,單位為秒。上面結果中,有效期是20天(1728000秒),即允許緩存該條回應1728000秒(即20天),在此期間,不用發出另一條預檢請求。
4.3 瀏覽器的正常請求和回應
一旦服務器通過了"預檢"請求,以后每次瀏覽器正常的CORS請求,就都跟簡單請求一樣,會有一個Origin頭信息字段。服務器的回應,也都會有一個Access-Control-Allow-Origin頭信息字段
下面是"預檢"請求之后,瀏覽器的正常CORS請求。
PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0.
上面頭信息的Origin字段是瀏覽器自動添加的。
下面是服務器正常的回應。
Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8
上面頭信息中,Access-Control-Allow-Origin字段是每次回應都必定包含的。
2:其他服務端處理,比如nginx做代理,
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Tcp/Http請求流程

1:首先明確一下Tcp層是沒有請求這一說法的,Tcp是一種通信的方式,請求一詞是事務上的概念,HTTP是一種事務協議,所以說,HTTP請求這種說法沒錯,但是Tcp請求是錯誤的,

2TCP/IP協議的三次握手和四次分手,用一張網絡上的圖來說明

clipboard.png

上面這個圖是網絡上面長說的,下面我們使用wireshark來具體看下 TCP/IP請求

clipboard.png

wireshark:抓包明顯看到TCP的多次握手流程

客戶端進行一次HTTP請求具體流程如下:

1:客戶端發送一次握手SYN到服務端,

2:這個時候服務端接收到客戶端的請求,把請求放入到SYN隊列里面,並且向客戶端返回SYN+ack j+1信息

3:客戶端收到服務端的返回信息再次確認,然后再次發送請求到服務端,ack k+1,這個時候服務端吧SYN隊列里面的請求取出來,然后插入到ACCEPT隊列中,

上面三次操作是服務端系統做的事,具體請求還沒有到我們自己的程序,只有到了ACCEPT隊列,然后我們程序服務器比如iis,或者tomcat去處理ACCEPT請求才能吧請求轉入到我們程序

比如我們NIO編程,服務端開始程序就是

clipboard.png

循環監控ACCEPT如果有確認好的請求(三次握手成功的)就拿出來,轉到我們程序處理

而且通過wireshark抓包我們看到不管是請三次握手還是后面的四次分手,都是TCP方式,只有建立成功連接發送請求數據的時候是HTTP請求,TCP報文承載了HTTP協議的請求(Request)和響應(Response)

 

GetPost的區別

首先我們先看一下HTTP協議

Http請求的請求協議:分為三部分,

第一部分是請求行,主要是請求方式,請求url和請求參數,用空格分隔,當讀取到回車(\r\n)的時候說明請求行讀取完成

第二部分是請求頭:是鍵值對,主要是一些定制的頭信息,cookie和自己設置的信息,用:分隔,當讀取到回車(\r\n)的時候說明請求行讀取完成

第三部分是請求體:這個主要是我們請求的數據,具體數據怎么操作在請求頭上面有說明Content-type

 

clipboard.png

Http響應的響應協議

clipboard.png

通過我們上面的TCP協議和HTTP協議我們可以看到,GetPost請求本質是一樣的,都需要三次握手和四次分手,而且都是Http協議,所以協議信息也是一樣的 ,

但是getpost具體的區別是什么那 ,還是發送協議的時候信息不一樣,我們知道一個get請求比如http://xxx/api/go_userInfo/getuserinfolist?_appid=app&fields=rolelist,headimage,nickname,newnickname,isbindwlt,userid,sex&useridlist=,40636794

這樣一個請求在請求協議的請求行的url里面就已經吧我們的請求數據一起發送了,所以不需要我們在發送請求體來發送我們請求數據,但是這樣做有也一定的確定因為url能承載的數據是有限的,一般瀏覽器承載是2k

服務器默認是64k

clipboard.png

但是post不同,post請求的請求行的url是協議+虛擬目錄不包含我們具體的數據,所以他的數據是通過請求體來發送的,因為是通過請求體來發送數據所以可以發送很大的數據,而且可以發送很多次

(如果數據太大,一次發送不完,可以多次發送,都是在一個請求內)

clipboard.png

所以說get請求只需要發送一次就可以完成數據的存取,但是post因為要發送請求體,所以可能需要一次以上的請求,不同瀏覽器的處理不同,有的瀏覽器post會發送兩個包,吧請求行和請求頭作為一次發送

請求體作為一次發送,但是有的瀏覽器值發送一次比如(火狐)

 

長連接

短連接:一次請求就釋放的連接【比較簡單,不用管理連接狀態,維護連接,因為當前連接都是可用的連接】

長連接:完成一期請求不釋放,保持這個鏈接,為后面的請求復用【比較復雜,需要維護連接的狀態,和鏈接心跳,】

下面我們再來說一下長連接【TCP 本身並沒有長短連接的區別,長短與否,完全取決於我們怎么用它】,Http長連接和服務端長連接是不一樣的 ,從名字我們可以明白,所謂長連接,指在一個TCP連接上可以連續發送多個數據包,在TCP連接保持期間,如果沒有數據包發送,需要雙方發檢測包以維持此連,短連接是指通信雙方有數據交互時,就建立一個TCP連接,數據發送完成后,則斷開此TCP連接,Tcp連接只要兩個ip建立了連接那就是連接了沒有長短之分,並且連接的兩個ip都可以主動斷開連接,也都可以主動推送或者拉取數據,是全雙工的

但是Http協議規定,請求是半雙工的,就是只能有客戶端來主動獲取數據,主動斷開,

Http協議長連接:在1.1以后http協議已經默認開啟了長連接,但是這種長連接是為了連接的復用,畢竟我們一次http請求要三次握手和四次分手,這個是很耗費時間的 ,比如說我們有一個頁面,里面有一下幾個請求,http://www.xxx.com/aaa, http://www.xxx.com/bbb, http://www.xxx.com/ccc

當我們瀏覽器發送http請求的時候比如我們先請求了http://www.xxx.com/aa這個請求,如果不是http長連接 和如何,先三次握手然后發送Http請求http://www.xxx.com/aaa然后在四次分手,一次請求結束,然后在同樣的流程去請求下一個,這樣會很耗費時間和性能,

如果是http長連接又會怎么處理那:客戶端先三次握手,然后發送Http請求http://www.xxx.com/aaa請求結束后我們不會進行四次分手,而是吧這個連接保持着,然后在發送http://www.xxx.com/bbbhttp://www.xxx.com/ccc,當這個鏈接保持一段時間沒有在發送任何請求的時候在

釋放掉,進行四次分手,這就是Http協議的長連接

 

Post請求Content-Type的四種方式

Content-Type:是用來告訴服務端有什么方式來解析請求體,

1Content-Type: application/x-www-form-urlencoded; charset=UTF-8最常見的方式,如果不設置 Content-Type:默認就是這個, application/x-www-form-urlencoded是使用 url編碼來解析請求體

# python腳本 import requests url = "http://httpbin.org/post" data = {"name":"西園公子","age":"666"} headers = {"Content-type":"application/x-www-form-urlencoded"} content = requests.post(url=url,data=data,).text print(content)  # 網絡請求: POST http://httpbin.org/post HTTP/1.1 Host    httpbin.org User-Agent  python-requests/2.24.0 Accept-Encoding gzip, deflate Accept  */* Content-Length  49 Content-Type    application/x-www-form-urlencoded Connection  keep-alive  # 下面是表單內容 name=%E8%A5%BF%E5%9B%AD%E5%85%AC%E5%AD%90&age=666      # 可以看出漢字是使用utf8編碼的

2multipart/form-data (鍵值對型數據)

這種方式是想服務端提交文件,或者請求數據中包含文件,客戶端會生成一個boundary 來做內容分割

import requests from requests_toolbelt import MultipartEncoder  m = MultipartEncoder(     fields={'field0': 'value1', 'field1': 'value2', 'field2': ('filename', open('data.txt', 'rb'), 'text/plain')}     )  content = requests.post('http://httpbin.org/post', data=m,                   headers={'Content-Type': m.content_type}).text  print(content) print(m.content_type)   # 1、網絡請求: POST http://httpbin.org/post HTTP/1.1 Host: httpbin.org User-Agent: python-requests/2.24.0 Accept-Encoding: gzip, deflate Accept: */* Content-Type: multipart/form-data; boundary=e48c73a7a42e403d868095dc3d060962 Content-Length: 222 Connection: keep-alive  # 下面是編碼的表單內容 --e48c73a7a42e403d868095dc3d060962 Content-Disposition: form-data; name="field0"  value1 --e48c73a7a42e403d868095dc3d060962 Content-Disposition: form-data; name="field1"  value2 --e48c73a7a42e403d868095dc3d060962-- Content-Disposition: form-data; name="field2"; filename="filename" Content-Type: text/plain  ä½ å¥½ï¼Œè¥¿å›­å…¬å­ï½ž --25c88ddc918d40e7a3cd5be0d62476b7--

 

如上面Content-Type: multipart/form-data; boundary=e48c73a7a42e403d868095dc3d060962,其中e48c73a7a42e403d868095dc3d060962 是請求體的內容分隔符

分隔符后面跟着的是鍵值對的建的名稱,也就是我們的參數名 如:Content-Disposition: form-data; name="field0"中的field0

然后讀取到換行符,在緊跟着就是鍵值對的值了如value1,當讀取到下一個分隔符 ,或者數據讀取完了,表明當前數據的鍵值對數據讀取完整

 

3application/json (Json 類型數據)

# 1、原生網絡請求 POST /post HTTP/1.1 Host: httpbin.org User-Agent: python-requests/2.24.0 Accept-Encoding: gzip, deflate Accept: */* Content-Type: application/json Content-Length: 62 Connection: keep-alive  # 下面是編碼成json數據 的表單內容 "{\"name\": \"\\u516c\\u5b50\\u54e5\", \"hobby\": \"coding\"}"

 

4text/xml xml

import requests # from requests_toolbelt import MultipartEncoder p_data = """ <?xml version="1.0"?> <methodCall>     <methodName>examples.getStateName</methodName>     <params>         <param>             <value><i4>41</i4></value>         </param>     </params> </methodCall> """ content = requests.post(url='http://httpbin.org/post',data=p_data,headers={'Content-Type':'text/xml'}).text  print(content)    # 2、打印數據 {   "args": {},    "data": "\n<?xml version=\"1.0\"?>\n<methodCall>\n    <methodName>examples.getStateName</methodName>\n    <params>\n        <param>\n            <value><i4>41</i4></value>\n        </param>\n    </params>\n</methodCall>\n",    "files": {},    "form": {},    "headers": {     "Accept": "*/*",      "Accept-Encoding": "gzip, deflate",      "Content-Length": "200",      "Content-Type": "text/xml",      "Host": "httpbin.org",      "User-Agent": "python-requests/2.24.0",      "X-Amzn-Trace-Id": "Root=1-60024d51-775180ce108409b413dc68c6"   },    "json": null,    "origin": "106.33.40.219",    "url": "http://httpbin.org/post" }

傳遞的是xml

 

 

Http跨域

首先要說明的是HTTP協議本身是沒有跨域請求的設置的,跨域請求的限制是瀏覽器為了安全考慮加上去的【服務端同源策略】。

然后我們要了解什么算跨域  我們知道一個url分為  協議://域名:端口,這樣組成,如果不寫端口默認是80,這三部分之中任何一部分不一樣就會形成跨域

瀏覽器提供了兩種方式的跨域請求

1:瀏覽器不限制script標簽、img標簽、link標簽的跨域請求,所以我們可以使用js在頁面加載的時候 生成script標簽來做跨域請求,但是這種請求只能是get的,如下

 var s = document.createElement('script');     var b = false;     s.setAttribute('type', 'text/javascript');     s.setAttribute('src', args);     s.id = "tempscript" + result;

 

2CORS跨域資源共享(CORS)

sever返回的Response Head中添加Access-Control-Allow-Origin

瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)只要同時滿足以下兩大條件,就屬於簡單請求。

 

簡單請求

1) 請求方法是以下三種方法之一:

l  HEAD

l  GET

l  POST

2HTTP的頭信息不超出以下幾種字段:

l  Accept

l  Accept-Language

l  Content-Language

l  Last-Event-ID

l  Content-Type:只限於三個值application/x-www-form-urlencodedmultipart/form-datatext/plain

這是為了兼容表單(form),因為歷史上表單一直可以發出跨域請求。AJAX 的跨域設計就是,只要表單可以發,AJAX 就可以直接發。凡是不同時滿足上面兩個條件,就屬於非簡單請求。

瀏覽器對這兩種請求的處理,是不一樣的。

 

對於簡單請求:

瀏覽器直接發出CORS請求。具體來說,就是在頭信息之中,增加一個Origin字段來說明,本次請求來自哪個源(協議 + 域名 + 端口)。服務器根據這個值,決定是否同意這次請求。

如果Origin指定的源,不在許可范圍內,服務器會返回一個正常的HTTP回應。瀏覽器發現,這個回應的頭信息沒有包含Access-Control-Allow-Origin字段就知道出錯了,

從而拋出一個錯誤,被XMLHttpRequestonerror回調函數捕獲。注意,這種錯誤無法通過狀態碼識別,因為HTTP回應的狀態碼有可能是200

如果Origin指定的域名在許可范圍內,服務器返回的響應,會多出幾個頭信息字段

Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8

Access-Control-Allow-Origin該字段是必須的。它的值要么是請求時Origin字段的值,要么是一個*,表示接受任意域名的請求

Access-Control-Allow-Credentials該字段可選。它的值是一個布爾值,表示是否允許發送Cookie。默認情況下,Cookie不包括在CORS請求之中。設為true,即表示服務器明確許可,Cookie可以包含在請求中,一起發給服務器。這個值也只能設為true,如果服務器不要瀏覽器發送Cookie,刪除該字段即可。

Access-Control-Expose-Headers該字段可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必須在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar字段的值。

withCredentials

CORS請求默認不發送CookieHTTP認證信息。如果要把Cookie發到服務器,一方面要服務器同意,指定Access-Control-Allow-Credentials=true字段。

另一方面,開發者必須在AJAX請求中打開withCredentials屬性。var xhr = new XMLHttpRequest(); xhr.withCredentials = true;

否則,即使服務器同意發送Cookie,瀏覽器也不會發送。或者,服務器要求設置Cookie,瀏覽器也不會處理。

但是,如果省略withCredentials設置,有的瀏覽器還是會一起發送Cookie。這時,可以顯式關閉

 

非簡單請求

4.1 預檢請求

非簡單請求是那種對服務器有特殊要求的請求,比如請求方法是PUTDELETE,或者Content-Type字段的類型是application/json

 

非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。

 

瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可以使用哪些HTTP動詞和頭信息字段。只有得到肯定答復,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。

 

下面是一段瀏覽器的JavaScript腳本。

 

 

var url = 'http://api.alice.com/cors';

var xhr = new XMLHttpRequest();

xhr.open('PUT', url, true);

xhr.setRequestHeader('X-Custom-Header', 'value');

xhr.send();

上面代碼中,HTTP請求的方法是PUT,並且發送一個自定義頭信息X-Custom-Header

 

瀏覽器發現,這是一個非簡單請求,就自動發出一個"預檢"請求,要求服務器確認可以這樣請求。下面是這個"預檢"請求的HTTP頭信息。

 

 

OPTIONS /cors HTTP/1.1

Origin: http://api.bob.com

Access-Control-Request-Method: PUT

Access-Control-Request-Headers: X-Custom-Header

Host: api.alice.com

Accept-Language: en-US

Connection: keep-alive

User-Agent: Mozilla/5.0...

"預檢"請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。頭信息里面,關鍵字段是Origin,表示請求來自哪個源。

 

除了Origin字段,"預檢"請求的頭信息包括兩個特殊字段。

 

1Access-Control-Request-Method

 

該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,上例是PUT

 

2Access-Control-Request-Headers

 

該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段,上例是X-Custom-Header

 

4.2 預檢請求的回應

服務器收到"預檢"請求以后,檢查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段以后,確認允許跨源請求,就可以做出回應。

 

 

HTTP/1.1 200 OK

Date: Mon, 01 Dec 2008 01:15:39 GMT

Server: Apache/2.0.61 (Unix)

Access-Control-Allow-Origin: http://api.bob.com

Access-Control-Allow-Methods: GET, POST, PUT

Access-Control-Allow-Headers: X-Custom-Header

Content-Type: text/html; charset=utf-8

Content-Encoding: gzip

Content-Length: 0

Keep-Alive: timeout=2, max=100

Connection: Keep-Alive

Content-Type: text/plain

上面的HTTP回應中,關鍵的是Access-Control-Allow-Origin字段,表示http://api.bob.com可以請求數據。該字段也可以設為星號,表示同意任意跨源請求。

 

 

Access-Control-Allow-Origin: *

如果服務器否定了"預檢"請求,會返回一個正常的HTTP回應,但是沒有任何CORS相關的頭信息字段。這時,瀏覽器就會認定,服務器不同意預檢請求,因此觸發一個錯誤,被XMLHttpRequest對象的onerror回調函數捕獲。控制台會打印出如下的報錯信息。

 

 

XMLHttpRequest cannot load http://api.alice.com.

Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

服務器回應的其他CORS相關字段如下。

 

 

Access-Control-Allow-Methods: GET, POST, PUT

Access-Control-Allow-Headers: X-Custom-Header

Access-Control-Allow-Credentials: true

Access-Control-Max-Age: 1728000

1Access-Control-Allow-Methods

 

該字段必需,它的值是逗號分隔的一個字符串,表明服務器支持的所有跨域請求的方法。注意,返回的是所有支持的方法,而不單是瀏覽器請求的那個方法。這是為了避免多次"預檢"請求。

 

2Access-Control-Allow-Headers

 

如果瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,表明服務器支持的所有頭信息字段,不限於瀏覽器在"預檢"中請求的字段。

 

3Access-Control-Allow-Credentials

 

該字段與簡單請求時的含義相同。

 

4Access-Control-Max-Age

 

該字段可選,用來指定本次預檢請求的有效期,單位為秒。上面結果中,有效期是20天(1728000秒),即允許緩存該條回應1728000秒(即20天),在此期間,不用發出另一條預檢請求。

 

4.3 瀏覽器的正常請求和回應

一旦服務器通過了"預檢"請求,以后每次瀏覽器正常的CORS請求,就都跟簡單請求一樣,會有一個Origin頭信息字段。服務器的回應,也都會有一個Access-Control-Allow-Origin頭信息字段。

 

下面是"預檢"請求之后,瀏覽器的正常CORS請求。

 

 

PUT /cors HTTP/1.1

Origin: http://api.bob.com

Host: api.alice.com

X-Custom-Header: value

Accept-Language: en-US

Connection: keep-alive

User-Agent: Mozilla/5.0...

上面頭信息的Origin字段是瀏覽器自動添加的。

 

下面是服務器正常的回應。

 

 

Access-Control-Allow-Origin: http://api.bob.com

Content-Type: text/html; charset=utf-8

上面頭信息中,Access-Control-Allow-Origin字段是每次回應都必定包含的。

 

2:其他服務端處理,比如nginx做代理,

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM