1.1 回顧網(wǎng)絡(luò)編程
1.2 http協(xié)議介紹
HTTP是Hyper Text Transfer Protocol(超文本傳輸協(xié)議)的縮寫棉钧。它的發(fā)展是萬維網(wǎng)協(xié)會(World Wide Web Consortium)和Internet工作小組IETF(Internet Engineering Task Force)合作的結(jié)果,(他們)最終發(fā)布了一系列的RFC涕蚤,RFC 1945定義了HTTP/1.0版本宪卿。其中最著名的就是RFC 2616。RFC 2616定義了今天普遍使用的一個版本——HTTP 1.1万栅。
HTTP協(xié)議(HyperText Transfer Protocol佑钾,超文本傳輸協(xié)議)是用于從WWW服務(wù)器傳輸超文本到本地瀏覽器的傳送協(xié)議。它可以使瀏覽器更加高效申钩,使網(wǎng)絡(luò)傳輸減少次绘。它不僅保證計算機正確快速地傳輸超文本文檔,還確定傳輸文檔中的哪一部分撒遣,以及哪部分內(nèi)容首先顯示(如文本先于圖形)等邮偎。
HTTP是一個基于TCP/IP通信協(xié)議來傳遞數(shù)據(jù)(HTML文件,圖片文件,查詢結(jié)果等)。
HTTP是一個應(yīng)用層協(xié)議义黎,由請求和響應(yīng)構(gòu)成禾进,是一個標(biāo)準(zhǔn)的客戶端服務(wù)器模型。HTTP是一個無狀態(tài)的協(xié)議廉涕。
HTTP協(xié)議永遠都是客戶端發(fā)起請求泻云,服務(wù)器回送響應(yīng)。見下圖:
這樣就限制了使用HTTP協(xié)議狐蜕,無法實現(xiàn)在客戶端沒有發(fā)起請求的時候宠纯,服務(wù)器將消息推送給客戶端。
HTTP協(xié)議是一個無狀態(tài)的協(xié)議层释,同一個客戶端的這次請求和上次請求是沒有對應(yīng)關(guān)系婆瓜。
1.3 使用谷歌或者火狐瀏覽器分析
在Web應(yīng)用中,服務(wù)器把網(wǎng)頁傳給瀏覽器,實際上就是把網(wǎng)頁的HTML代碼發(fā)送給瀏覽器廉白,讓瀏覽器顯示出來个初。而瀏覽器和服務(wù)器之間的傳輸協(xié)議是HTTP,所以:
·HTML是一種用來定義網(wǎng)頁的文本猴蹂,會HTML院溺,就可以編寫網(wǎng)頁;
·HTTP是在網(wǎng)絡(luò)上傳輸HTML的協(xié)議磅轻,用于瀏覽器和服務(wù)器的通信珍逸。
Chrome瀏覽器提供了一套完整地調(diào)試工具,非常適合Web開發(fā)瓢省。
安裝好Chrome瀏覽器后弄息,打開Chrome,在菜單中選擇“視圖”勤婚,“開發(fā)者”摹量,“開發(fā)者工具”,就可以顯示開發(fā)者工具:
說明
·Elements顯示網(wǎng)頁的結(jié)構(gòu)
·Network顯示瀏覽器和服務(wù)器的通信
我們點Network馒胆,確保第一個小紅燈亮著缨称,Chrome就會記錄所有瀏覽器和服務(wù)器之間的通信:
1.4 http協(xié)議分析
當(dāng)我們在地址欄輸入www.sina.com時,瀏覽器將顯示新浪的首頁祝迂。在這個過程中睦尽,瀏覽器都干了哪些事情呢?通過Network的記錄型雳,我們就可以知道当凡。在Network中,找到www.sina.com那條記錄纠俭,點擊沿量,右側(cè)將顯示Request Headers,點擊右側(cè)的view source冤荆,我們就可以看到瀏覽器發(fā)給新浪服務(wù)器的請求:
1.5瀏覽器請求
說明
最主要的頭兩行分析如下朴则,第一行:
GET / HTTP/1.1
GET表示一個讀取請求,將從服務(wù)器獲得網(wǎng)頁數(shù)據(jù)钓简,/表示URL的路徑乌妒,URL總是以/開頭,/就表示首頁外邓,最后的HTTP/1.1指示采用的HTTP協(xié)議版本是1.1撤蚊。目前HTTP協(xié)議的版本就是1.1,但是大部分服務(wù)器也支持1.0版本损话,主要區(qū)別在于1.1版本允許多個HTTP請求復(fù)用一個TCP連接拴魄,以加快傳輸速度。
從第二行開始,每一行都類似于Xxx: abcdefg:
Host: www.sina.com
表示請求的域名是www.sina.com匹中。如果一臺服務(wù)器有多個網(wǎng)站,服務(wù)器就需要通過Host來區(qū)分瀏覽器請求的是哪個網(wǎng)站豪诲。
說明
我們可以跟數(shù)據(jù)庫的CRUD增刪改查操作對應(yīng)起來:
1.CREATE:PUT
2.READ:GET
3.UPDATE:POST
DELETE:DELETE
1.6 服務(wù)器響應(yīng)
繼續(xù)往下找到Response Headers顶捷,點擊view source,顯示服務(wù)器返回的原始響應(yīng)數(shù)據(jù):
HTTP響應(yīng)分為Header和Body兩部分(Body是可選項)屎篱,我們在Network中看到的Header最重要的幾行如下:
HTTP/1.1 200 OK
200表示一個成功的響應(yīng)服赎,后面的OK是說明。
如果返回的不是200交播,那么往往有其他的功能重虑,例如
·失敗的響應(yīng)有404 Not Found:網(wǎng)頁不存在
·500 Internal Server Error:服務(wù)器內(nèi)部出錯
·...等等...
詳情請點擊HTTP狀態(tài)碼.jpg
Content-Type: text/html
Content-Type指示響應(yīng)的內(nèi)容,這里是text/html表示HTML網(wǎng)頁秦士。
請注意缺厉,瀏覽器就是依靠Content-Type來判斷響應(yīng)的內(nèi)容是網(wǎng)頁還是圖片,是視頻還是音樂隧土。瀏覽器并不靠URL來判斷響應(yīng)的內(nèi)容提针,所以,即使URL是http://www.baidu.com/meimei.jpg曹傀,它也不一定就是圖片辐脖。
HTTP響應(yīng)的Body就是HTML源碼,我們在菜單欄選擇“視圖”皆愉,“開發(fā)者”嗜价,“查看網(wǎng)頁源碼”就可以在瀏覽器中直接查看HTML源碼:
瀏覽器解析過程
當(dāng)瀏覽器讀取到新浪首頁的HTML源碼后,它會解析HTML幕庐,顯示頁面久锥,然后,根據(jù)HTML里面的各種鏈接翔脱,再發(fā)送HTTP請求給新浪服務(wù)器奴拦,拿到相應(yīng)的圖片、視頻届吁、Flash错妖、JavaScript腳本、CSS等各種資源疚沐,最終顯示出一個完整的頁面暂氯。所以我們在Network下面能看到很多額外的HTTP請求。
1.7 總結(jié)
1.7.1 請求流程
跟蹤了新浪的首頁亮蛔,我們來總結(jié)一下HTTP請求的流程:
步驟1:瀏覽器首先向服務(wù)器發(fā)送HTTP請求痴施,請求包括:
方法:GET還是POST,GET僅請求資源,POST會附帶用戶數(shù)據(jù);
路徑:/full/url/path动知;
域名:由Host頭指定:Host: www.sina.com
以及其他相關(guān)的Header明垢;
如果是POST,那么請求還包括一個Body厘惦,包含用戶數(shù)據(jù)
步驟2:服務(wù)器向瀏覽器返回HTTP響應(yīng),響應(yīng)包括:
響應(yīng)代碼:200表示成功哩簿,3xx表示重定向宵蕉,4xx表示客戶端發(fā)送的請求有錯誤,5xx表示服務(wù)器端處理時發(fā)生了錯誤节榜;
響應(yīng)類型:由Content-Type指定羡玛;
以及其他相關(guān)的Header;
通常服務(wù)器的HTTP響應(yīng)會攜帶內(nèi)容宗苍,也就是有一個Body稼稿,包含響應(yīng)的內(nèi)容,網(wǎng)頁的HTML源碼就在Body中浓若。
步驟3:如果瀏覽器還需要繼續(xù)向服務(wù)器請求其他資源渺杉,比如圖片,就再次發(fā)出HTTP請求挪钓,重復(fù)步驟1是越、2。
Web采用的HTTP協(xié)議采用了非常簡單的請求-響應(yīng)模式碌上,從而大大簡化了開發(fā)倚评。當(dāng)我們編寫一個頁面時,我們只需要在HTTP請求中把HTML發(fā)送出去馏予,不需要考慮如何附帶圖片天梧、視頻等,瀏覽器如果需要請求圖片和視頻霞丧,它會發(fā)送另一個HTTP請求呢岗,因此,一個HTTP請求只處理一個資源(此時就可以理解為TCP協(xié)議中的短連接蛹尝,每個鏈接只獲取一個資源后豫,如需要多個就需要建立多個鏈接)
![](http://i1.sinaimg.cn/home/2013/1008/U8455P30DT20131008135420.png)
HTTP協(xié)議同時具備極強的擴展性,雖然瀏覽器請求的是http://www.sina.com的首頁突那,但是新浪在HTML中可以鏈入其他服務(wù)器的資源挫酿,比如,從而將請求壓力分散到各個服務(wù)器上愕难,并且早龟,一個站點可以鏈接到其他站點惫霸,無數(shù)個站點互相鏈接起來,就形成了World Wide Web葱弟,簡稱WWW壹店。
e7 a??D? ?
1.7.2 http格式
每個HTTP請求和響應(yīng)都遵循相同的格式,一個HTTP包含Header和Body兩部分芝加,其中Body是可選的茫打。
HTTP協(xié)議是一種文本協(xié)議,所以妖混,它的格式也非常簡單。
HTTP GET請求的格式:
GET /path HTTP/1.1
Header1: Value1
Header2: Value2
Header3: Value3
每個Header一行一個轮洋,換行符是\r\n制市。
HTTP POST請求的格式:
POST /path HTTP/1.1
Header1: Value1
Header2: Value2
Header3: Value3
body data goes here...
當(dāng)遇到連續(xù)兩個\r\n時,Header部分結(jié)束弊予,后面的數(shù)據(jù)全部是Body祥楣。
HTTP響應(yīng)的格式:
200 OK
Header1: Value1
Header2: Value2
Header3: Value3
body data goes here...
HTTP響應(yīng)如果包含body,也是通過\r\n\r\n來分隔的汉柒。
請再次注意误褪,Body的數(shù)據(jù)類型由Content-Type頭來確定,如果是網(wǎng)頁碾褂,Body就是文本兽间,如果是圖片,Body就是圖片的二進制數(shù)據(jù)正塌。
當(dāng)存在Content-Encoding時嘀略,Body數(shù)據(jù)是被壓縮的,最常見的壓縮方式是gzip乓诽,所以帜羊,看到Content-Encoding: gzip時,需要將Body數(shù)據(jù)先解壓縮鸠天,才能得到真正的數(shù)據(jù)讼育。壓縮的目的在于減少Body的大小,加快網(wǎng)絡(luò)傳輸稠集。
1.8 web靜態(tài)服務(wù)器
1.8.1 顯示靜態(tài)頁面
import socket
from multiprocessing import Process
def handleClient(clientSocket):
'用一個新的進程奶段,為一個客戶端進行服務(wù)'
recvData = clientSocket.recv(2014)
requestHeaderLines = recvData.splitlines()
for line in requestHeaderLines:
print(line.decode('utf-8'))
responseHeaderLines = "HTTP/1.1 200 OK\r\n"
responseHeaderLines += "Server:laowang\r\n"
responseHeaderLines += "\r\n"
responseBody = "hello world"
response = responseHeaderLines + responseBody
clientSocket.send(bytes(response,'gbk'))
clientSocket.close()
def main():
'作為程序的主控制入口'
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serverSocket.bind(("", 8000))
serverSocket.listen(10)
while True:
clientSocket,clientAddr = serverSocket.accept()
clientP = Process(target = handleClient, args = (clientSocket,))
clientP.start()
clientSocket.close()
if __name__ == '__main__':
main()
客戶端瀏覽器
服務(wù)端
1.9顯示需要的頁面
參考代碼
import socket
import re
from multiprocessing import Process
#設(shè)置靜態(tài)文件根目錄
HTML_ROOT_DIR = "./html"
def handle_client(client_socket):
"""處理客戶端請求"""
#獲取客戶端請求數(shù)據(jù)
request_data = client_socket.recv(1024)
print("request data:", request_data)
request_lines = request_data.splitlines()
for line in request_lines:
print(line)
#解析請求報文
# 'GET / HTTP/1.1'
request_start_line = request_lines[0].decode('utf-8')
#提取用戶請求的文件名
file_name = re.match(r"\w+ +(/[^ ]*) ", request_start_line).group(1)
if "/" == file_name:
file_name = "/index.html"
#打開文件,讀取內(nèi)容
try:
file = None
file = open(HTML_ROOT_DIR + file_name, "rb")
file_data = file.read()
#構(gòu)造響應(yīng)數(shù)據(jù)
response_start_line = "HTTP/1.1 200 OK\r\n"
response_headers = "Server: My server\r\n"
response_body = file_data.decode("utf-8")
except FileNotFoundError:
response_start_line = "HTTP/1.1 404 Not Found\r\n"
response_headers = "Server: My server\r\n"
response_body = "The file is not found!"
finally:
if file and (not file.closed):
file.close()
response = response_start_line + response_headers + "\r\n" + response_body
print("response data:", response)
#向客戶端返回響應(yīng)數(shù)據(jù)
client_socket.send(bytes(response, "utf-8"))
#關(guān)閉客戶端連接
client_socket.close()
def main():
'作為程序的主控制入口'
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(("", 8000))
server_socket.listen(128)
while True:
client_socket, client_address = server_socket.accept()
# print("[%s, %s]用戶連接上了" % (client_address[0],client_address[1]))
print("[%s, %s]用戶連接上了" % client_address)
handle_client_process = Process(target=handle_client, args=(client_socket,))
handle_client_process.start()
client_socket.close()
if __name__ == "__main__":
main()
客戶瀏覽器