Python是一個很強大的網絡編程工具,python內有不少針對場景網絡協議的庫,在庫頂部能夠得到抽象層,這樣就能夠集中精力在程序的邏輯處理上,而不是停留在網絡實現的細節中。javascript
1 少數幾個網絡設計模塊html
在標準庫中有不少網絡設計模塊,在其餘地方還有更多,下面是經常使用的模塊。java
在網絡編程中一個基本組件是套接字(socket,又是哪一個人才翻譯的),套接字是兩個程序之間的信息通道,python所以了socket模塊的基本細節,並不直接和套接字交互。套接字包括兩個:服務器套接字和客戶機套接字,建立一個服務器套接字,讓它等待鏈接,這樣它就在某個網絡地址處(IP+端口號)監聽。處理客戶端套接字一般比處理服務器端套接字容易,由於服務器端隨時處理客戶端的鏈接,還有處理多個鏈接,而客戶機只是簡單地鏈接,完成事務,斷開鏈接。
socket模塊中的socket方法用於建立套接字,socket方法語法格式以下:
socket(socket_family, socket_type, protocol = 0)
其中:
socket_family:該參數的值能夠爲AF_UNIX或AF_INET
socket_type:該參數的值能夠爲SOCK_STREAM或SOCK_DGRAM
protocol:該參數通常不賦值,默認值爲0
套接字至關於應用程序訪問下層網絡服務的接口,使用套接字,使得用戶能夠在不一樣的主機之間進行通訊,從而實現數據交換。python
一個正在被使用的套接字有着和其匹配的通訊類型及相關的進程信息,而且會和另一個套接字交換數據。當前Socket規範支持兩種類型的套接字,即流套接字和數據報套接字,其中,流套接字提供了雙向有序且不重複的數據服務。而數據報套接字雖然支持雙向的套接字,可是對報文的可靠性和有序性不能保證。換句話說,經過數據報套接字接口獲取的數據有多是重複的,這須要上層的應用程序來進行區分,另外還有更多的套接字類型,好比原始套接字類型,這和系統中的實現相關。
一個套接字就是一個Socket模塊中的Socket類的實例,它的實例化須要3個參數:
1.第一個參數是地址簇,默認值是socket.AF_INET
2.第二個參數是流(默認值是socket.SOCK_STREAM)或數據報(默認值是socket.SOCK_DGRAM)套接字
3.第三個參數是使用的協議,默認值爲0,使用默認值便可。
不一樣的套接字類型有着不一樣的套接字地址,在AF_UNIX地址簇中使用一個簡單的字符串;在AF_INET地址簇中使用的是host和port地址對,其中host爲主機地址名,IP地址或url,而port是一個整數;AF_INET6地址簇中用(host,port,flowinfo,scopeid)元組來表示,其中的host和port和AF_INET相同。linux
客戶端應用程序在生成套接字對象後,能夠調用bind()方法來綁定本身的請求套接字接口地址,而後調用connect方法來鏈接服務器端進程。當鏈接創建後,可使用send和recv方法來傳輸數據,而後使用close方法將端口關閉。web
對於服務端套接字來講,使用bind方法綁定一個套接字接口地址,接着使用listen方法監聽客戶端請求,當有客戶端請求時,經過accept方法來生成一個鏈接對象,而後經過此鏈接對象發送和接收數據,數據傳輸完畢,能夠調用close方法將生成的鏈接關閉。服務器端通訊過程的方法調用以下:數據庫
urllib和urllib2是很是強大的庫,它容許經過網絡訪問文件,這些文件像存儲在本地電腦上同樣,只須要一個簡單的方法調用,幾乎能夠把任何URL所指向的文件用程序訪問。
urllib2是更強大的庫,若是隻使用簡單的下載,urllib就足夠了,若是須要使用HTTP驗證或Cookie以及須要爲協議寫擴展程序,那麼urllib2是不錯的選擇。urllib2是python的一個獲取url的模塊,它用urlopen方法提供了一個很是簡潔的接口,使得用各類各樣的協議獲取url成爲可能。同時提供了稍微複雜的接口來處理常見的情況,例如基本的認證、cookies和代理等,這些都是由openner和handler的對象來處理。編程
能夠像打開本地文件同樣打開遠程文件,不一樣之處是可使用只讀模式,使用的是來自urllib2模塊中的urlopen方法,該方法格式以下:
urlopen(url, data = None,proxies= None)
方法說明:
url:符合url規範的字符串
data:向指定url發送的數據字符串,get和post均可以,但必須符合標準格式,格式爲key = value
proxies:代理服務器地址字典,若是未指定,在windows平臺上依據IE設置,不支持須要驗證的代理服務器。如proxies={"http","htttp://www.python.org:8080"}
下面介紹用urlopen打開遠程文件windows
import urllib2 response = urllib2.urlopen("http://10.137.168.14:11100/store/web/portalone"); html = response.read(); print html
這段代碼中,首先使用urllib2模塊中的urlopen方法打開了一個遠程文件,返回一個類文件對象,該類文件對象支持close、read、readlines和readline方法,同時也支持迭代,本例中用read方法打開遠程文件,返回url指向的頁面源碼瀏覽器
<script type="text/javascript"> $(function(){ if(musicPlay) { musicPlay.hide(); } }); </script>
若是須要訪問本地文件,則能夠用file開頭的URL做爲urlopen方法。
import urllib2 response = urllib2.urlopen(r"file:///home/jack/demo/4.5/hell/Demo09.py"); html = response.read(); print html
用urlopen提供一個能從中讀取數據的類文件對象,若是須要urllib下載所指向的文件並在本地文件中存儲一個文件副本,那麼就可使用urllib模塊中的urlretrieve方法,urlretrieve方法返回一個元組(filename,headers),而不是類文件對象,其中filename是本地文件的名字由urllib自動建立,headers包含一些遠程文件的信息,urlretrieve方法語法格式以下:
urlretrieve(url, filename=None,reporthook=None,data=None)
該方法說明以下:
url:符合URL規範的字符串
filename:本地文件路徑的字符串,從URL返回的數據將保存在該文件中,若是設置爲None,則聲稱一個臨時文件
reporthook:一個方法的引用,能夠任意自定義該方法的行爲,只須要保證方法有3個參數:第一個參數爲目前爲止傳遞的數據庫數量:第二個參數爲每一個數據塊的大小,單位爲byte:第三個參數爲文件的總大小
data:向指定的url發送字符串,get和post均可以,必須符合標準格式:key=value
下面介紹如何使用urlretrieve方法獲取遠程文件:
import urllib; urllib.urlretrieve(r"http://www.cnblogs.com/people", r"F:\Current Study\python\people.html")
該程序將一個網頁http://www.cnblogs.com/people 下載下來,並以people.html名稱保存。
python的其餘模塊
服務端和客戶端通訊是構建網絡通訊的基礎,例如HTTP訪問,它也是客戶端和服務端的通訊,即瀏覽器從HTTP服務器獲取HTML數據並顯示。
下面先建立一個服務端程序。
上面的listen方法有一個參數,用來設置鏈接隊列的長度
如前所述,用Socket生成一個服務進程相對比較複雜,爲了簡化,python專門提供了SocketServer模塊,此模塊有5個服務類。以下所示:
通常狀況下,BaseServer類不會被實際使用,所以SocketServer模塊中只包含了4個基本的類:針對TCP套接字流的TCPServer,針對UDP數據報套接字的UDPServer,以及UNIXStreamServer和UNIXDatagramServer,其中最重要的類是TCPServer,此類中有簡單TCP協議實現的服務器接口,爲應用程序提供了可靠的流數據傳輸,除此以外,還有更多模塊中的類派生於TCPServer類,包括BaseHTTPServer、SimpleHTTPServer、CGIHTTPServer等等,經過對SocketServer模塊中類的繼承,能夠實現更多功能的服務端應用程序,接着是UDPServer類,此類中有數據報服務的接口。此類一樣提供數據傳輸服務,可是並不保證數據的可靠性和有序性。另外兩個使用較少的是UNIXStreamServer和UNIXDatagramServer類,這兩個類都是使用UNIX域套接字地址,他們幾乎不能使用在非UNIX平臺上。
爲了寫一個使用SocketServer框架的服務器,大部分代碼會包含在一個請求處理程序中,每當服務器收到一個請求時,就會實例化一個請求處理程序,而且它的各類處理方法會在 處理請求時被調用。基本的BaseRequestHander類把全部的操做都放到了handle方法中,這個方法會被服務器調用,而後訪問屬性self.request中的客戶端套接字。若是使用的是流,那麼可使用StreamRequestHander類來建立兩個新屬性self.rfile和self.wfile,而後使用這些類文件對象和客戶機通訊。
SocketServer框架中的其餘類實現了對HTTP服務器的基本支出,其中包括容許CGI腳本,也包括對XML RPC的支持。
下面用SocketServer模塊製做一個小型服務器:
客戶端的構建
import socket; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); host = socket.gethostname(); port = 1234; s.connect((host, port)); print s.recv(1024); s.close;
對於一個客戶端而言,首先用Socket模塊導入,而後調用其socket方法生成一個Socket對象,並使用gethostname方法獲取服務器地址,以後再經過connect方法鏈接服務程序,當客戶端鏈接服務器後,一直等待的服務進程被喚醒,並處理此鏈接,這裏的客戶端處理直接調用recv方法獲取服務端發送過來的數據,最後調用close方法將鏈接關閉。運行:
Link come from ('192.168.1.103', 4642)
2.3.2 使用urlparse包也能夠建立客戶端
Socket套接字不只能夠鏈接服務器,還能夠處理客戶端請求,從而達到服務端與客戶端的通訊。下面用Socket對象分別建立一個服務端和客戶端。
服務端socket流程是:
首先生成Socket對象,而後使用bind方法綁定主機和端口號,並使用listen方法監聽,以後進行while循環,使服務器一直處於監聽狀態,使用accept方法能夠接受客戶端的一個鏈接,接着使用send方法來向客戶端發送一個字符串的數據,最後使用close鏈接,釋放資源。
總的來講:先建立socket鏈接並監聽,而後阻塞等待,若是有消息過來,就發送消息,而後關閉鏈接,總共4步
服務端:
客戶端:
客戶端的流程是:使用Socket模塊中的socket方法生成socket對象,而後使用connect鏈接服務器,並輸出服務端發送過來的消息,最後關閉Socket對象。
import socket; import time; sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM); sock.connect(("localhost", 8001)); time.sleep(2); sock.send("1"); print sock.recv(1024); sock.close();
在上面的示例中,全部的服務器端實現都是同步的,也就是說,服務程序只有處理完一個鏈接後,才能處理另一個鏈接。若是要使服務器端應用程序可以同時處理多個鏈接,則須要使用異步通訊方式。python標準庫提供了三種處理方式:分叉方式、線程方式和異步I/O方式。
當有多個鏈接同時到達服務器端時,能夠經過分叉方式進行處理,對於接收到的每一個鏈接,主進程都會生成相應的一個子進程專門用來處理此鏈接,而主進程則依舊保持在監聽狀態,從而使每一個鏈接都由一個對應的子進程來處理。因爲生成的子進程和主進程是同時運行的,因此不會阻塞新的鏈接。這種方式好處是比較簡單、有效,可是因爲生成進程消耗的資源比較大,因此當鏈接不少時,會帶來性能問題。
分叉是至關於複製了主進程,可是隻是子進程,至關於在時間線上建立了分支,最後獲得兩個獨立存在的進程,進程能夠判斷哪一個是主進程,哪一個是子進程(使用fork,就行叉子同樣)
在一個使用分叉的服務器中,每個客戶端鏈接都利用分叉創造一個子進程,主進程繼續監聽新的鏈接,同時子進程處理客戶端,當客戶端請求結束後,子進程就退出了,由於分叉的進程是並行運行的,客戶端之間沒必要相互等待。
在該例子中定義了Server類,該類繼承自ForkingMixIn和TCPServer類,使此類具備兩個類的特色,也就是提供流數據傳輸服務和多鏈接處理。
因爲線程是一種輕量級的進程,具備進程所沒有的優點,當存在大量鏈接而消耗資源太多的狀況下,則可使用線程方式進行處理。線程實現的方式和分叉的處理方式相似。當有鏈接到來的時候,主線程將生成一個子線程來處理鏈接,而在子線程處理鏈接的時候,主線程依然處於監聽狀態,並不會阻塞鏈接。
因爲生成的子線程和主線程都存在於相同的進程中,共享內存,所以這種處理方式的效率很是高,從而使得大量地使用線程會形成線程之間的數據同步,若是處理很差,則可能使得服務程序時區效應,這就必須確保主線程和子線程的變量不衝突,在現代操做系統中通常都使用分叉方式來處理多鏈接,可是windows不支持。
下面是利用分叉建立線程的方式:
這段代碼與使用分叉處理的示例相同,惟一區別在於在生成的Server類時採用了ThreadingMixIn類,這樣生成的Server類在處理多鏈接時採用線程方式處理
當服務器與客戶端通訊時,來自客戶端的數據持續的時間較長且數據突發的多鏈接狀況下,若是使用分叉或線程處理,佔用資源太多,一種改進的方式就是採用專門的異步IO通訊方式,即在必定的時間段查看已有的鏈接並處理。處理的過程包括讀取數據和發送數據。
在python標準庫中,由asyncore和asynchat模塊來實現異步IO處理,這種功能依賴於select()和poll方法,這兩個方法定義在select模塊中。
注意:select和poll方法,poll方法的伸縮性更好,但它只能在UNIX系統中使用,在windows系統中不可用。
1.select模塊
select方法用於指定的文件描述符進行監視,並在文件描述符集改變的時候作出響應。select模塊中的select方法的語法格式以下:
select.select(List rlist, List wlist, List xlist[, long timeout])
select方法有4個參數:
1.rlist、wlist和xlist,這是3個必須參數,分別表示等待輸入、輸出和錯誤的文件描述符,這3個參數都爲文件描述符列表。空列表也是容許的,可是若是3個參數都是空列表,則表示平臺相關,在linux系統平臺下容許,在windows平臺下不容許。
2.timeout是可選的,參數爲一個浮點數,用例指定系統監視文件描述符集改變的超時時間,單位爲妙。
select方法返回值是一個包含3個值的元組,元組中的3個值即爲在select方法中前3個參數已經準備好的文件描述符。下面介紹select方法
2.使用asyncore模塊
在python中,可使用asyncore模塊來實現異步通訊,實際上,該模塊提供了用例構建異步通訊方式的客戶端和服務端的基礎架構,特別適用於聊天類的服務器和協議的實現。其基本思想是,建立一個或多個網絡信道,實際上網絡信道是Socket對象的一個封裝,當信道建立後,調用loop方法來激活網絡信道服務,直到最後一個網絡信道關閉。
在asyncore模塊中,主要用於網絡事件循環檢測的loop方法是核心,在loop方法中將會經過select方法來檢測特定的網絡信道。當select方法返回全部事件的socket對象後,loop方法檢查檢查此事件和套接字狀態並建立一個高層次的事件信息,而後針對此高層次的事件信息調用相應的方法,asyncore還提供了底層的API用來建立服務器。
同時,該模塊中還有一個dispatcher類,這是一個Socket對象的輕量級封裝,用於處理網絡交互事件,其中的方法是在異步loop方法中調用或者直接看成一個普通的非阻塞Socket對象。框架是:
在dispatcher類中,當在特定的時間或連續狀態條件下,會觸發一些高層次的事件,在繼承類中能夠經過重載這些方法來處理這些特定的事件,默認事件以下:
handle_connect:鏈接時的訪問接口
handle_close:接口關閉
handle_accept:從監聽端口上獲取數據
注意:在進行異步處理的過程當中,能夠經過網絡信道的readable和writeable方法來對事件進行控制,使用這兩個方法,可以判斷是否須要使用select或poll方法來讀取事件
在asyncore模塊中,readable和writable默認不作任何操做,而直接訪問True,能夠經過重載這兩個方法來判斷須要檢查的臉頰,從而控制流程及網絡狀態。以後可使用handle_read和handle_write方法來讀寫網絡數據,完成對數據的接收和發送。下面是一個HTTP協議的客戶端例子:
在該段代碼中,導入了asyncore模塊和socket模塊,接着定義了一個名稱爲http_client的類,該類繼承自asyncore.dispatcher類,所以能夠在http_client類中重載dispatcher類中的處理方法。
在構造函數中,首先調用了dispatcher類的構造函數,而後用create_socket方法建立socket對象,該方法封裝了socket模塊的socket方法。在調用socket方法以後,還用了setblocking方法設置其阻塞方式爲非阻塞,並獲取了套接字的文件描述符最後經過add_channel方法將文件描述符加入。在構造函數中使用connect方法鏈接特定服務器的80端口,這也是http默認端口,並設置類變量buffer爲一個HTTP獲取命令,獲取內容。
接下來定義了5個事件處理方法,分別在不一樣的時間發生時候被調用。
1.handle_connect方法:將在HTTP鏈接時候被調用
2.handle_close方法:關閉鏈接
3.handle_read方法:調用recv方法來獲取http數據,recv方法中的參數爲一次讀取最大字節數,須要注意的是,緩衝區大小最好爲2的冪,如1024或者4086等數據
4.handle_write方法:用來處理髮送時的數據,這裏首先調用send方法發送數據,其返回值爲依據發送成功的數據,而後設置buffer爲未發送的數據,這樣作的緣由是在異步通訊過程當中,不必定能保證每次發送都能發送成功
5.writable方法:用來判斷在何時發送數據,在方法體重,只是判斷須要發送數據的緩衝區是否爲空,若是不爲空,則返回True,表示須要發送數據,而當緩衝區爲空時,則不須要繼續發送數據。
示例:使用asyncore和socket模塊將所請求的服務器端信息打印出來
步驟:1.導入asyncore和socket模塊2.新建一個類,該類繼承asyncore.dispatcher類,並重載asyncore.dispatcher類中的__init__、handle_connect、handle_read、writable、handle_write和handle_close方法。在__init__方法中獲取與服務端的臉頰,並將指定的頁面請求賦值與request對象;在handle_connect方法中輸出鏈接的服務器信息;在handle_write方法中獎請求對象request發送到服務器端;在handle_read方法中獎獲取的服務器發送給客戶端的信息寫入一個記事本中。