Python 網(wǎng)絡(luò)編程說明第2/2頁
更新時間:2009年08月25日 01:10:10 作者:
socket 是網(wǎng)絡(luò)連接端點。
例如,對于一個聊天室來說,因為有多個連接需要同時被處理,所以很顯然,阻塞或同步的方法是不合適的,這就像買票只開了一個窗口,佷多人排隊等一樣。那么我們?nèi)绾谓鉀Q這個問題呢?主要有三種方法:forking、threading、異步I/O。
Forking和threading的方法非常簡單,通過使用SocketServer服務(wù)類的min-in類就可以實現(xiàn)。forking只適用于類Unix平臺;threading需要注意內(nèi)存共享的問題。
異步I/O如果底層的方法來實現(xiàn)是有點困難的。要簡單點,我們可以考慮使用標(biāo)準(zhǔn)庫中的框架或Twisted(Twisted是一個非常強大的異步網(wǎng)絡(luò)編程的框架)。
一、用ScoketServer實現(xiàn)Forking和threading
下面我們使用兩個例子來分別創(chuàng)建forking服務(wù)器和threading服務(wù)器。
Forking 服務(wù)器:
from SocketServer import TCPServer, ForkingMixIn, StreamRequestHandler
class Server(ForkingMixIn, TCPServer): pass
class Handler(StreamRequestHandler):
def handle(self):
addr = self.request.getpeername()
print 'Got connection from', addr
self.wfile.write('Thank you for connecting')
server = Server(('', 1234), Handler)
server.serve_forever()
threading服務(wù)器:
from SocketServer import TCPServer, ThreadingMixIn, StreamRequestHandler
class Server(ThreadingMixIn, TCPServer): pass
class Handler(StreamRequestHandler):
def handle(self):
addr = self.request.getpeername()
print 'Got connection from', addr
self.wfile.write('Thank you for connecting')
server = Server(('', 1234), Handler)
server.serve_forever()
二、使用select實現(xiàn)異步I/O
所謂異步I/O,打個比方,就是如果一大群人都想你聽他說話,那么你就給他們每人一分鐘的時間說,大家輪流說,沒說完的待會兒輪到時再繼續(xù)說。也就是一個時間片的方法。
要實現(xiàn)異步I/O,我們可以通過使用框架asyncore/asynchat或Twisted,它們都是基于select函數(shù)或poll函數(shù)(poll只適于類Unix系統(tǒng))的。select和poll函數(shù)都來自select模塊。
select 函數(shù)要求三個必須序列作為參數(shù)和一個可選的以秒為單位的超時值。序列中是表示文件描述符的整數(shù)值,它們是我們要等待的連接。這三個序列是關(guān)于輸入、輸出和 異常條件的。如果超時值沒有給出的話,select將處于阻塞狀態(tài)(也就是等待)直到有文件描述符準(zhǔn)備動作。如果超時值給出了,那么select只阻塞給 定的時間。如果超時值是0的話,那么將不阻塞。select返回的值是一個由三個序列組成的元組,它們分別代表相應(yīng)參數(shù)的活動的子集。例如,第一個序列返 回的是用于讀的輸入文件描述符構(gòu)成的序列。
序列可以包含文件對象(不適于Windows)或socket。下面這個例子創(chuàng)建一個使用 select去服務(wù)幾個連接的服務(wù)器(注意:服務(wù)端的socket自身也提供給了select,以便于它能夠在有新的連接準(zhǔn)備接受時發(fā)出信號通知)。這個 服務(wù)器只是簡單地打印接受自客戶端的數(shù)據(jù)。你可以使用telnet(或?qū)懸粋€基于socket的簡單的客戶端)來連接測試它。
select server
import socket, select
s = socket.socket()
host = socket.gethostname()
port = 1234
s.bind((host, port))
s.listen(5)
inputs = [s]
while True:
rs, ws, es = select.select(inputs, [], [])
for r in rs:
if r is s:
c, addr = s.accept()
print 'Got connection from', addr
inputs.append(c)
else:
try:
data = r.recv(1024)
disconnected = not data
except socket.error:
disconnected = True
if disconnected:
print r.getpeername(), 'disconnected'
inputs.remove(r)
else:
print data
三、Twisted
Twisted 是針對Python的一個事件驅(qū)動的網(wǎng)絡(luò)框架,最初是為了網(wǎng)絡(luò)游戲而開發(fā)的,但是現(xiàn)在被應(yīng)用于各類網(wǎng)絡(luò)軟件。用Twisted,你可以實現(xiàn)事件處理器,非 常類似用GUI工具包(Tk, GTK, Qt, wxWidgets)。這部分我將介紹一些基本的概念和演示如何使用Twisted來做一些相對簡單的 網(wǎng)絡(luò)編程。Twisted是非常強大的框架并提供了大量的支持,如:Web服務(wù)器和客戶端、 SSH2, SMTP, POP3, IMAP4, AIM, ICQ, IRC, MSN,Jabber, NNTP, DNS等等。
早先我們所寫的基于socket的服務(wù)器,它們都有一個顯示的事件循環(huán):尋找新的連接和新的數(shù)據(jù);基于SocketServer的服務(wù)器有一個隱含的循環(huán):尋找連接和為連接創(chuàng)建處理器。但時處理器仍然時顯示的讀數(shù)據(jù)。
而 Twisted使用了更多的基于事件的方式。要寫一個基本的服務(wù)器,你要實現(xiàn)事件處理器,它處理諸如一個新的客戶端連接、新的數(shù)據(jù)到達和客戶端連接中斷等 情況。在Twisted中,你的事件處理器定義在一個protocol中;你也需要一個factory,當(dāng)一個新的連接到達時它能夠構(gòu)造這個 protocol對象,但是如果你僅僅想創(chuàng)建一個自定義的Protocol類的實例的話,你可以使用來自Twisted的factory,F(xiàn)actory 類在模塊twisted.internet.protocol中。當(dāng)你寫你的protocol時,使用 twisted.internet.protocol模塊中的Protocol作為你的父類。當(dāng)你得到一個連接時,事件處理器 connectionMade被調(diào)用;當(dāng)你丟失了一個連接時,connectionLost被調(diào)用。從客戶端接受數(shù)據(jù)使用處理器 dataReceived。但是你不能使用事件處理策略向客戶端發(fā)送數(shù)據(jù);要向客戶端發(fā)送數(shù)據(jù),你可以使用self.transport,它有一個 write方法。它也有一個client屬性,其中包含了客戶端的地址(主機名和端口)。
下面這個例子是一個Twisted版的服務(wù)器。 其中實例化了Factory并設(shè)置了它的protocol屬性以便它知道使用哪個protocol與客戶端通信(這就是所謂的你的自定義 protocol)。然后你使用factory開始監(jiān)聽指定的端口,factory通過實例化的protocol對象處理連接。監(jiān)聽使用reactor模 塊中的listenTCP函數(shù)。最后,你通過調(diào)用reactor模塊中的run函數(shù)來開始服務(wù)器。
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, Factory
# 定義你Protocol類
class SimpleLogger(Protocol):
def connectionMade(self):
print 'Got connection from', self.transport.client
def connectionLost(self, reason):
print self.transport.client, 'disconnected'
def dataReceived(self, data):
print data
# 實例化Factory
factory = Factory()
# 設(shè)置factory的protocol屬性以便它知道使用哪個protocol與客戶端通信(這就是所謂的你的自定義
# protocol)
factory.protocol = SimpleLogger
# 監(jiān)聽指定的端口
reactor.listenTCP(1234, factory)
# 開始運行主程序
reactor.run()
為 你的處理目的而寫一個自定義的protocol是很容易的。模塊twisted.protocols.basic中包含了幾個有用的已存在的 protocol,其中的LineReceiver執(zhí)行dataReceived并在接受到了一個完整的行時調(diào)用事件處理器lineReceived。如 果當(dāng)你在接受數(shù)據(jù)時除了使用lineReceived,還要做些別的,那么你可以使用LineReceiver定義的名為rawDataReceived 事件處理器。下面是一使用LineReceiver的服務(wù)器例子:
from twisted.internet import reactor
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
class SimpleLogger(LineReceiver):
def connectionMade(self):
print 'Got connection from', self.transport.client
def connectionLost(self, reason):
print self.transport.client, 'disconnected'
def lineReceived(self, line):
print line
factory = Factory()
factory.protocol = SimpleLogger
reactor.listenTCP(1234, factory)
reactor.run()
urllib和urllib2
urllib 和urllib2的工作大同小異,它們讓你能夠通過網(wǎng)絡(luò)訪問文件,就像訪問自己電腦上的一樣。通過簡單的函數(shù)調(diào)用,URL所定位的資源就可以被你作為輸入 使用到你的程序中。如果再配以re模塊,那么你就能夠下載Web頁面、提取信息、自動創(chuàng)建你所尋找的東西的報告。
urllib2更流行一些。對于簡單的下載任務(wù),urllib比較好。如果你需要HTTP驗證或cookies,或你想寫一些擴展去處理你自己的協(xié)議的話,那么urllib2是正確的選擇。
一、打開遠程文件
打開遠程文件的操作和本地差不多,不同的是只能使用讀模式,并且使用urllib模塊的urlopen:
>>> from urllib import urlopen
>>> webpage=urlopen('http://www.python.org')
如果你在線的話,變量webpage現(xiàn)在就包含了一個關(guān)聯(lián)Web頁:http://www.python.org的文件類對象。
注意:如果你當(dāng)前沒有聯(lián)網(wǎng),而你又想練習(xí)一下urllib的話,你可以用如下形式訪問本地文件:
localpage=urlopen(r'file:c:\test.txt')
由urlopen返回的文件類對象支持close,read,readline,readlines等方法。
下面的代碼抽取出了Python官方主頁中“Documentation”鏈接的URL:
>>> import re
>>> text = webpage.read()
>>> m = re.search('<a href="([^"]+)">Documentation</a>', text, re.IGNORECASE)
>>> m.group(1)
'http://docs.python.org/'
二、獲取遠程文件
urlopen 函數(shù)給你一個文件類對象,你可以讀取它。如果你使用urlib時只關(guān)心下載文件并存儲一個復(fù)本到本地文件的話,你可以使用urlretrieve替而代 之。urlretrieve返回一個元組(filename, headers),filename是本地文件(復(fù)本)的名字(它由urllib自動創(chuàng) 建),headers包含關(guān)于遠程文件的一些信息。
如果你想為復(fù)本指定一個名字的話,你可以提供第二個參數(shù):
urlretrieve('http://www.python.org', 'C:\\python_webpage.html')
這 將獲取Python官方主頁并存儲到本地C:\python_webpage.html中。如果你不指定復(fù)本的文件名,那么文件將放到一個臨時的地方,你 能夠使用open函數(shù)打開它,如果你要清除這些臨時的復(fù)本,你可以調(diào)用urlcleanup函數(shù)而不帶任何參數(shù),它將為你完成清除工作。
一、套接字
套接字是為特定網(wǎng)絡(luò)協(xié)議(例如TCP/IP,ICMP/IP,UDP/IP等)套件對上的網(wǎng)絡(luò)應(yīng)用程序提供者提供當(dāng)前可移植標(biāo)準(zhǔn)的對象。它們允許程序接受并進行連接,如發(fā)送和接受數(shù)據(jù)。為了建立通信通道,網(wǎng)絡(luò)通信的每個端點擁有一個套接字對象極為重要。
套接字為BSD UNIX系統(tǒng)核心的一部分,而且他們也被許多其他類似UNIX的操作系統(tǒng)包括Linux所采納。許多非BSD UNIX系統(tǒng)(如ms-dos,windows,os/2,mac os及大部分主機環(huán)境)都以庫形式提供對套接字的支持。
三種最流行的套接字類型是:stream,datagram和raw。stream和datagram套接字可以直接與TCP協(xié)議進行接口,而raw套接字則接口到IP協(xié)議。但套接字并不限于TCP/IP。
二、套接字模塊
套接字模塊是一個非常簡單的基于對象的接口,它提供對低層BSD套接字樣式網(wǎng)絡(luò)的訪問。使用該模塊可以實現(xiàn)客戶機和服務(wù)器套接字。要在python 中建立具有TCP和流套接字的簡單服務(wù)器,需要使用socket模塊。利用該模塊包含的函數(shù)和類定義,可生成通過網(wǎng)絡(luò)通信的程序。一般來說,建立服務(wù)器連接需要六個步驟。
第1步是創(chuàng)建socket對象。調(diào)用socket構(gòu)造函數(shù)。
socket=socket.socket(familly,type)
family的值可以是AF_UNIX(Unix域,用于同一臺機器上的進程間通訊),也可以是AF_INET(對于IPV4協(xié)議的TCP和 UDP),至于type參數(shù),SOCK_STREAM(流套接字)或者 SOCK_DGRAM(數(shù)據(jù)報文套接字),SOCK_RAW(raw套接字)。
第2步則是將socket綁定(指派)到指定地址上,socket.bind(address)
address必須是一個雙元素元組,((host,port)),主機名或者ip地址+端口號。如果端口號正在被使用或者保留,或者主機名或ip地址錯誤,則引發(fā)socke.error異常。
第3步,綁定后,必須準(zhǔn)備好套接字,以便接受連接請求。
socket.listen(backlog)
backlog指定了最多連接數(shù),至少為1,接到連接請求后,這些請求必須排隊,如果隊列已滿,則拒絕請求。
第4步,服務(wù)器套接字通過socket的accept方法等待客戶請求一個連接:
connection,address=socket.accept()
調(diào)用accept方法時,socket會進入'waiting'(或阻塞)狀態(tài)。客戶請求連接時,方法建立連接并返回服務(wù)器。accept方法返回一個含有倆個元素的元組,形如(connection,address)。第一個元素(connection)是新的socket對象,服務(wù)器通過它與客戶通信;第二個元素(address)是客戶的internet地址。
第5步是處理階段,服務(wù)器和客戶通過send和recv方法通信(傳輸數(shù)據(jù))。服務(wù)器調(diào)用send,并采用字符串形式向客戶發(fā)送信息。send方法返回已發(fā)送的字符個數(shù)。服務(wù)器使用recv方法從客戶接受信息。調(diào)用recv時,必須指定一個整數(shù)來控制本次調(diào)用所接受的最大數(shù)據(jù)量。recv方法在接受數(shù)據(jù)時會進入'blocket'狀態(tài),最后返回一個字符串,用它來表示收到的數(shù)據(jù)。如果發(fā)送的量超過recv所允許,數(shù)據(jù)會被截斷。多余的數(shù)據(jù)將緩沖于接受端。以后調(diào)用recv時,多余的數(shù)據(jù)會從緩沖區(qū)刪除。
第6步,傳輸結(jié)束,服務(wù)器調(diào)用socket的close方法以關(guān)閉連接。
建立一個簡單客戶連接則需要4個步驟。
第1步,創(chuàng)建一個socket以連接服務(wù)器 socket=socket.socket(family,type)
第2步,使用socket的connect方法連接服務(wù)器 socket.connect((host,port))
第3步,客戶和服務(wù)器通過send和recv方法通信。
第4步,結(jié)束后,客戶通過調(diào)用socket的close方法來關(guān)閉連接。
三、一個簡單的服務(wù)器和客戶端通信的例子
服務(wù)器:
import socket
s=socket.socket()
s.bind(('xxx.xxx.xxx.xxx',xxxx)) #ip地址和端口號
s.listen(5)
cs,address = s.accept()
print 'got connected from',address
cs.send('byebye')
ra=cs.recv(512)
print ra
cs.close()
客戶端:
import socket
s=socket.socket()
s.connect(('xxx.xxx.xxx.xxx',xxxx)) #與服務(wù)器程序ip地址和端口號相同
data=s.recv(512)
s.send('hihi')
s.close()
print 'the data received is',data
運行:
在本機測試(windows環(huán)境下,可以將ip地址改為本機ip,端口號在1024以上,windows將1024以下的為保留),運行--CMD--進入命令行模式
先python 服務(wù)器程序,后python 客戶端程序即可。
或者啟動服務(wù)器程序后,用telnet ip地址 端口號,也可以得到同樣結(jié)果。
--------------------------------------------------------------------------------
讓server持續(xù)接受連接
server.py
import socket
s=socket.socket()
s.bind(('192.168.43.137',2000))
s.listen(5)
while 1:
cs,address = s.accept()
print 'got connected from',address
cs.send('hello I am server,welcome')
ra=cs.recv(512)
print ra
cs.close()
測試兩個一個程序中兩個socket并存是否可行
client.py
import socket
s=socket.socket()
s.connect(('192.168.43.137',2000))
data=s.recv(512)
print 'the data received is\n ',data
s.send('hihi I am client')
sock2 = socket.socket()
sock2.connect(('192.168.43.137',2000))
data2=sock2.recv(512)
print 'the data received from server is\n ',data2
sock2.send('client send use sock2')
sock2.close()
s.close()
網(wǎng)絡(luò)編程框架2009年04月12日 星期日 上午 10:39twisted是python里面公認(rèn)的很牛的網(wǎng)絡(luò)編程框架。學(xué)python網(wǎng)絡(luò)編程的如果不學(xué)twisted,估計也就只能算是了解python網(wǎng)絡(luò)編 程吧,就如同開發(fā)網(wǎng)站要用django是一樣的,二者都是python下有名的框架。twisted是基于單線程的事件驅(qū)動的網(wǎng)絡(luò)引擎。關(guān)于它的學(xué)習(xí)資料 比較少,而且中文的就更少了,所以學(xué)習(xí)twisted一定要硬著頭皮看英文文檔,也就是它的twisted documentation,在這里基本可以找到你所需要的所有基礎(chǔ)知識。尤其是core documentation 和example里面都講了很多示例,這些示例如果都通通的運行一遍,那么你的twisted已經(jīng)可以算入門了。
我主要是用twisted的工廠和協(xié)議框架編寫了一個內(nèi)部的內(nèi)容分發(fā)網(wǎng)絡(luò)的Tracker服務(wù)器,不是基于標(biāo)準(zhǔn)bt協(xié)議的,如果要學(xué)習(xí),最好還是按照標(biāo)準(zhǔn)BT協(xié)議。前面也給了網(wǎng)址。至于如何使用twisted,我會在后續(xù)文章詳細(xì)介紹。
本文先介紹twisted的兩種工作方式,reactor 和 application方式。
The reactor is the core of the event loop within Twisted -- the loop which drives applications using Twisted. The reactor provides basic interfaces to a number of services, including network communications, threading, and event dispatching.
reactor是twisted事件循環(huán)的核心,它提供了一些服務(wù)的基本接口,像網(wǎng)絡(luò)通信、線程和事件的分發(fā)。
詳細(xì)的關(guān)于reactor的介紹見twisted core documentation里面的Low-Level Twisted一章的第一節(jié)Reactor Overview.里面詳細(xì)介紹了各種reactor的安裝和使用。
我所知道的reactor有以下幾個
reactor platform Usage
IOCPReactor win32 from twisted.internet import iocpreactor iocpreactor.reactor.install()
from twisted.internet import reactor
selectReactor win32, posix from twisted.internet import reactor
pollReactor posix from twisted.internet import pollreactor
pollreactor.install()
from twisted.internet import reactor
epollReactor linux2.6 from twisted.internet import epollreactor
epollreactor.install()
from twisted.internet import reactor
kqueueReactor BSD系列 from twisted.internet import kqreactor
kqreactor.install()
from twisted.internet import reactor
以上幾種就是使用最多的幾種reactor了,除了kqueueReactor我沒有使用過以外,其他的都使用過了。都能正常工作。建議編程序的時候?qū)崿F(xiàn)根據(jù)不同的平臺選擇最佳的reactor。
系統(tǒng)默認(rèn)使用的是selectreactor。
下面給出一個小例子:
from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor
### Protocol Implementation
# This is just about the simplest possible protocol
class Echo(Protocol):
def dataReceived(self, data):
"""As soon as any data is received, write it back."""
self.transport.write(data)
def main():
f = Factory()
f.protocol = Echo
reactor.listenTCP(8000, f)
reactor.run()
if __name__ == '__main__':
main()
您可能感興趣的文章:
- python網(wǎng)絡(luò)編程之TCP通信實例和socketserver框架使用例子
- python網(wǎng)絡(luò)編程之UDP通信實例(含服務(wù)器端、客戶端、UDP廣播例子)
- Python 網(wǎng)絡(luò)編程起步(Socket發(fā)送消息)
- python socket網(wǎng)絡(luò)編程步驟詳解(socket套接字使用)
- 用Python進行TCP網(wǎng)絡(luò)編程的教程
- python網(wǎng)絡(luò)編程調(diào)用recv函數(shù)完整接收數(shù)據(jù)的三種方法
- python網(wǎng)絡(luò)編程實例簡析
- Python網(wǎng)絡(luò)編程中urllib2模塊的用法總結(jié)
- python 網(wǎng)絡(luò)編程詳解及簡單實例
- python網(wǎng)絡(luò)編程:socketserver的基本使用方法實例分析
相關(guān)文章
學(xué)習(xí)win32com操作word之Range精講
這篇文章主要為大家介紹了win32com操作word之Range精講學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-01-01Python浮點數(shù)取整、格式化和NaN處理的操作方法
這篇文章主要介紹了Python浮點數(shù)取整、格式化和NaN處理的操作方法,本文較詳細(xì)介紹了取整的三種方法,格式化浮點數(shù)輸出的示例代碼詳解,感興趣的朋友跟隨小編一起看看吧2022-05-05