網絡應用(8):http的封裝與使用

以前講過http的協議,怎麼約定請求或響應的行、頭、體,也介紹怎麼使用curl來完成http的請求。這一次,再接再礪,換一個角度換一些角色,再次說http的封裝與使用。反正目的只有一個:加深對http協議的理解。php

(1)tcp的實現

說http的實現,非講tcp不可(爲何?後面會解釋),而以前講tcp協議的使用,用到了socket,用socket就能通訊,就能作tcp想作的事情。html

那socket是什麼東西?java

用你的話來理解,socket是一個通道嗎?由於它能通訊啊。「socket是一個通道」已是抓到重點了,雖然不完整,由於socket在成爲通道前,並非通道,它只是一個等待使用的值。可是,「突出重點」的抽象是如此重要,不要追求完整或完美。socket,是網絡結點(ip:port),能夠表明數據通道,經過它就能夠在結點間傳送數據。 能夠想象,網絡上的一切數據交換,都由socket完成。python

socket並不僅是一個抽象的概念,它仍是實實在在的代碼實現,但你能看到的是二進制代碼,它已經給別人實現了,並且存在於各個平臺,好比windows、unix、ios、android等,那它在這些平臺是什麼樣的存在?它又是誰實現的呢?android

結合wiki等網頁的信息:ios

套接字起源於20世紀70年代加州大學伯克利分校版本的Unix,因此,這個套接字稱爲「伯克利套接字」或「BSD套接字」。 BSD sockets,是全部其它平臺的socket的祖先,包括windows/liunx,還有某些語言使用的socket好比python、java等。bsd socket用c語言實現,有對應的c層靜態庫(或動態庫)。web

windows的socket是Martin Hall、Mark Towfiq(從屬於Microsoft)等人的版權,有兩個大版本,即winsock1跟winsock2,對應的庫文件是wsock32.lib/ws2_32.lib,你在windows平臺上就是在使用這個庫文件進行socket操做。apache

好了,具體某些平臺是誰寫了socket的實現,就不深究了,只是說,感謝前輩們的貢獻。windows

那問題來了,socket實現了什麼?瀏覽器

socket實現了tcp協議(還有udp),這個很重要,對於http來講也很重要,由於http在協議棧上位於應用層,依賴於傳輸層的tcp協議,也就是說,http最終要用tcp來傳輸數據,而socket包括了tcp的實現,你說重不重要?

因此,http協議依靠socket來傳輸數據,但依靠還依靠,不表明http要理socket層的事情。

(1)http的實現

http跟tcp不一樣,http並不關心數據怎麼傳來傳去,反正能傳就行,這個是tcp層的事情,那http還要考慮什麼?就是請求與響應的格式啊,並且是人能讀懂的格式,http的要求已經很人性化了,這個對話已經很友好了(不像tcp的數據)。http報文的格式以前介紹了的!

對於http請求或響應數據,包括行、頭、體,其中行與頭是少不了的。那行頭體的格式由誰來封裝,注意,這不是socket的事情,socket無論這個,那誰管?

工具curl管http格式封裝(也管數據收發),java的HttpClient管http格式封裝,python的urllib也管http格式封裝,還有不少工具類或庫都能封裝http協議(注意,說http就只有http協議,不包括tcp,由於tcp是另外一層的事情),固然,你本身寫一個http的格式封裝,也沒有問題。

簡單來講,http的實現其實並不包括怎麼傳輸的事情,那是tcp層(socket)的事情,http的實現重點在於自己協議的封裝與實現,好比怎麼組裝請求頭、怎麼調用接口收發數據、怎麼解釋數據、GET或POST方式封裝、url編碼,等等。你會發現有不少零散的知識點,不少實現還跟業務關係密切,這也是http是應用層協議的表現。

對於你,如無必要,優先考慮已經穩定的http封裝實現吧,具體看你是什麼平臺進行對應的選擇。

(2)http的使用

以前講http的結構跟怎麼使用curl時,其實就是經過curl使用了http。是的,咱們並不會直接「使用http」,而是經過工具類來使用http,你能夠本身寫工具類好比弄一個httpclient的類出來,也可使用一些表現良好的現有的工具類,建議用後者。

這一次,我用python庫的一些工具,好比urllib或httplib,經過它們來使用http協議。

通常使用http,就是要向服務器發出請求,那服務器怎麼來?你天然能夠找一堆服務器出來,只要有對應的url就能夠,實際上你也只是請求某個url。可是,爲了顯得高級一點,我這裏自行定製一個web服務器,用來接收http請求。

如何定製web服務器?

小程在介紹「PHP程序」的使用時,把apache給引了進來,apache是一個web服務器,能夠接受你的http請求。另外一方面,apache能夠跟PHP解析器、python解析器等進行交互,能夠根據http請求,執行一些腳本代碼(像php腳本或python腳本等)。

這裏就演示一下,怎麼發起一個http請求到apache,apache再執行python腳本,返回http響應。

(a)讓apache能啓用python

按以前講解的知識,apache項目的目錄是:/Library/WebServer/Documents,以前php腳本就是放在這裏。可是,對於python腳本,解釋它的是cgi,這些腳本要放在cgi項目目錄,那個目錄是:/Library/WebServer/CGI-Executables,因此你寫的python腳本要放在這個目錄。 先無論那麼多,在CGI-Executables目錄下,建立一個hello.py,隨便寫點什麼,好比:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

title="小程"
info="你好,first py script,關注微信公衆號'廣州小程'"
htmlcontent="<html><head><meta charset='utf-8'><title>%s</title></head><body><h2>%s</h2></body></html>" % (title,info)
print("Content-type:text/html")
print("")
print(htmlcontent)

而後打開瀏覽器來請求一下這個文件吧(注意請求cgi-bin目錄),結果是這樣的:
apache返回腳本內容

顯然,這不是我想看到呀,我不是要看腳本代碼,我是想看到腳本代碼被解釋後的內容!

若是客戶端好比瀏覽器,向apache請求了python或php腳本,而apache沒有找到對應的腳本解釋器的話(注意apache本身是不懂腳本解釋的),apache會把這個腳本的內容完整地返回給瀏覽器,這時源碼暴露無遺,這極可能不是你想看到的事情。

因此,讓apache找到腳本解釋器(這裏是cgi),是相當重要的一步。

再也不講apache的啓用了,直接講,怎麼樣才能讓apache執行python腳本呢?你可能還記得,讓apache調用php程序的話,要把apache的配置文件httpd.conf改一下(/private/etc/apache2目錄下),也就是「LoadModule phpx_module...」這一行解除註釋,就能夠調用php程序了,那對於python是否是也這樣改呢?是的,bingo,也要解除註釋,這一句是:

#LoadModule cgi_module libexec/apache2/mod_cgi.so

把前面的#去掉並保存一下便可,注意重啓一下apache:

sudo apachectl restart

這時,apache就會加載mod_cgi.so,並用cgi來解釋python腳本。再用瀏覽器請求一次,看到這樣的返回:
apache返回正確的內容

簡單來講,cgi就是用來解釋python腳本的,apache啓用它就解決了python腳本解釋的問題,更多cgi的東西不說了。

(b)服務器處理http請求

至此,apache已經能夠執行我寫的python腳本了,那我寫這個hello.py來作什麼呢?固然是處理瀏覽器發過來的http請求了。因此,把hello.py再修改一下,讓它根據請求的參數值,返回不一樣的內容給瀏覽器,好比這樣:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import cgi

title="廣州小程"
info=""
paras = cgi.FieldStorage()
if paras.has_key('secret'):
    if paras['secret'].value=='free':
        info="<h1>welcome, you are so clever! secret is %s</h1>" % paras['secret'].value
    else:  
        info="<h1>wrong! secret is NOT '%s'</h>" % paras['secret'].value
else:
    info="<h1>NO! 請加上secret參數!</h1>"
htmlcontent="<html><head><meta charset='utf-8'><title>%s</title></head><body>%s</body></html>" % (title,info)
print("Content-type:text/html")
print("")

腳本中使用cgi.FieldStorage獲取請求參數,對這個參數給出了不一樣的提示。用瀏覽器請求一下,傳遞不一樣的參數,能夠看這樣的展現:
沒有secret參數時
secret參數參數不對時
secret參數正確時

至此,一切都是美好的,服務器腳本定好了,能應對請求了,瀏覽器也看到返回內容了,但是,我不是教你怎麼作web服務啊,我想說的是,怎麼使用http協議的呀!

(c)在python中使用http協議

這個纔是重點內容。這裏演示寫一個客戶端,用python裏面的類,完成http請求,以表示,我已經會使用http協議了!

分別用urllib2跟httplib來演示。

import urllib2
url='http://localhost/cgi-bin/hello.py?secret=free'
req=urllib2.Request(url)
res=urllib2.urlopen(req)
res=res.read()
print(res)

執行上面的代碼,使用urllib2這個http協議封裝類,發起請求,返回是這樣的:
urllib2的響應

import httplib
url='http://localhost/cgi-bin/hello.py?xx'
conn=httplib.HTTPConnection('localhost')
conn.request(method='GET', url=url)
res=conn.getresponse()
res=res.read()
conn.close()
print(res)

這一段代碼就形象不少了,先用HTTPConnection創建起連接(指定ip跟端口,端口省略意思就是用默認的80即web服務器的默認端口,另外個人localhost是固定到本機的),而後發起request,再取響應,一鼓作氣。執行結果是這樣的:
httplib的響應

以上請求都是get方式,post也是很容易的,你研究一下嘛。好了,已經會用http協議了,至少是用上了。

總結一下吧,本文主要講怎麼使用http協議,爲了說明白這個操做,講了tcp跟http的大道理,還講了怎麼讓apache解釋python腳本,最後那一點演示纔是重點,你至少知道有urllib這樣的類可使用http協議了吧!


what is import?

相關文章
相關標籤/搜索