Python 內置封裝了很多常見的網絡協議的庫,因此Python成為了一個強大的網絡編程工具,這里是對Python的網絡方面編程的一個簡單描述。
1. 常用的網絡設計模塊
在標准庫中有很多網絡設計相關的模塊,除了那些明確處理網絡事務的模塊外,還有很多模塊也是是和網絡相關的,下面是幾個常用的網絡設計模塊:
1.1 socket 模塊
socket
模塊是網絡編程中的基礎組件。socket 主要的作用就是作為兩個程序之間的“通信信道”,不同進程(不同主機)可以通過socket相互發送信息,以達到網絡通信的目的。socket 包括兩個部分:服務端和客戶端。服務端監聽端口號,等待客戶端發送的消息;而客戶端在需要發送信息是,連接服務端,將信息發送出去即可。下面是一個簡單的同步網絡編程的簡單示例:
這是Socket Server 部分:
import socket
s = socket.socket()
host = socket.gethostname()
port = 8088
s.bind((host,port))
s.listen(5)
while True:
c, addr = s.accept()
print 'Got connection from', addr
c.send('Thank you for connection')
c.close()
這是Socket Client 部分:
import socket
s = socket.socket()
host = socket.gethostname()
port = 8088
s.connect((host,port))
print s.recv(1024)
運行時,請將對應的端口(這里是8088)添加到防火牆的InBound和OutBound的規則中。
1.2 urllib 和 urllib2 模塊
urllib
和 urllib2
是Python標准庫中最強的的網絡工作庫。通過這兩個庫所提供的上層接口,使我們可以像讀取本地文件一樣讀取網絡上的文件。而且 urllib2
並不是 urllib
的升級版本(應該是一種補充),二者是不可相互替代的。
通過使用 urllib
的 urlopen
函數可以很容易的打開遠程的文件,如下:
from urllib import urlopen
webpage = urlopen('http://www.cnblogs.com/IPrograming/')
txt = webpage.readline(45)
print txt # !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
也可以通過在通過在路徑的前面添加 file:
來訪問本地文件:
from urllib import urlopen
webpage = urlopen(r'file:D:\H\sr23upd\ADD_ABBR.txt')
txt = webpage.readline(45)
print txt
如果你還可以通過 urllib
提供的 urlretrieve
函數,來直接保存遠程文件副本:
from urllib import urlretrieve
webpage = urlretrieve('http://www.cnblogs.com/IPrograming/','C:\\temp.html')
print type(webpage) # <type 'tuple'>
1.3 其他與網絡相關的模塊
除了 socket、urllib和urllib2這些模塊以外標准庫還有很多和網絡相關的模塊,下面的列表是其中的一部分:
===========================================================
模塊 描述
===========================================================
asynchat asyncore的增強版本
asyncore 異步socket處理程序
cgi 基本的CGI支持
Cookie Cookie對象操作,主要用於服務器操作
cookielib 客戶端cookie支持
email E-mail消息支持(包括MIME)
ftplib FTP客戶端模塊
gopherlib gopher客戶端博客
httplib HTTP客戶端模塊
imaplib IMAP4客戶端模塊
mailbox 讀取幾種郵件的格式
mailcap 通過mailcap文件訪問MIME配置
mhlib 訪問MH郵箱
nntplib NNTP客戶端模塊
poplib POP客戶端模塊
robotparser 支持解析Web服務器的robot文件
SimpleXMLRPCServer 一個簡單的XML-RPC服務器
stmpd SMTP服務器模塊
smtplib SMTP客戶端模塊
telnetlib Telnet客戶端模塊
urlparse 支持解析URL
xmlrpclib XML-RPC的客戶端支持
2. SocketServer
SocketServer模塊是標准庫中很多其他服務器框架的基礎,這些服務器框架包括:BaseHTTPServer、SimpleHTTPServer、CGIHTTPServer、SimpleXMLRPCServer和DocXMLRPCServer,這些服務框架都是在基礎框架上增加了特定的功能。SocketServer包含了4個基本的類:
- TCPServer,針對TCP的Socket
- UDPServer,針對UDP數據報的Socket
- UnixStreamServer
- UnixDatagramServer
下面是一個基於SocketServer的簡單Socket Server端示例:
from SocketServer import TCPServer, StreamRequestHandler
class Handler(StreamRequestHandler):
def handle(self):
addr = self.request.getpeername()
self.wfile.write('Thank you for connectiong')
server = TCPServer(('',8088),Handler)
server.serve_forever()
3. 多連接
一般情況下Socket中的Client端常常不止一個,想要使Socket Server端能同時處理多個Client的連接一般由三種主要的方法:
- 分叉(forking)(windows 不支持)
- 線程(threading)
- 異步I/O(asynchronous I/O)
3.1 使用分叉
分叉(fork)是一個UNIX術語;當分叉一個進程(一個運行的程序)時,基本上時復制了它,並且分叉后的兩個進程都從當前執行的點繼續運行,並且每個進程都有自己的內存副本。一個進程(開始的那個)成為另一個進程的(復制的,也就是子進程)的父進程。在一個使用分叉的服務器中,每個客戶端連接都利用分叉創建一個子進程。父進程繼續監聽連接,同時子進程處理客戶端。當客戶端的請求結束時,子進程退出。分叉的進程是並行執行的,客戶端直接不必相互等待。分叉的缺點是比較耗費資源(每個分叉出來的進程都需要自己的內存)。下面是一個使用分叉創建Socket服務端的示例:
# --coding:utf-8--
# 使用了分叉(fork),Windows系統不支持
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 connectiong')
server = Server(('',1234),Handler)
server.serve_forever()
3.2 使用線程
線程是輕量級的進程或子進程,所有的線程都存在於相同的進程(一個運行的程序)中,且共享內存。雖然使用多線程相對於分叉占用的資源較少,但是由於共享內存,所有必需要確保它們的變量不會沖突,或者是同一時間修改同一內容,這樣會造成混亂。這些問題可以歸結為同步問題。下面是使用多線程的一個簡單示例:
# --coding:utf-8--
# 使用多線程
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 connection')
server = Server(('',1234),Handler)
server.serve_forever()
3.3 帶有 select 和 poll 的異步I/O
在Python中的異步I/O的基礎就是 select
模塊的 select
函數。標准庫中的 asyncore
和 asynchat
模塊對它們進行了進一步的包裝,可以從更高層次來處理異步I/O。poll
函數和 select
函數一樣,也屬於 select
模塊,這兩個函數的功能基本一樣,相對而言 poll
的伸縮性更好,但其職能在UNIX系統使用使用。
select
函數需要3個序列作為它的必選參數(輸入、輸出、異常情況),第四個參數是可選的,表示以秒為單位的超時時間。下面是一個使用 select
的簡單示例:
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
poll
方法比 select
使用起來簡單,下面的時候就是上面示例的 poll
版本:
# -- coding: utf-8 --
# Windows 系統不支持poll
import socket, select
s = socket.socket()
host = socket.gethostname()
port = 1234
s.bind((host,port))
fdmap = {s.fileno(): s}
s.listen(5)
p = select.poll()
p.register(s)
while True:
events = p.poll()
for fd, event in events:
if fd in fdmap:
c, addr = s.accept()
print 'Got connection from', addr
p.register(c)
fdmap[c.fileno()] = c
elif event & select.POLLIN:
data = fdmap[fd].recv(1024)
if not data:# 如果沒有數據,關閉連接
print fdmap[fd].getpeername(), 'disconnected'
p.unregister(fd)
del fdmap[fd]
else:
print data
4. 使用Twisted
Twisted
是一個事件驅動的Python網絡框架。使用 Twisted
框架首先需要單獨下載安裝。我們可以使用pip
包管理工具來進行安裝,參考:http://www.cnblogs.com/IPrograming/p/Python_module_package.html#pip。下面是使用Twisted的兩個簡單示例:
4.1 使用 Twisted
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, Factory
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.protocol = SimpleLogger
reactor.listenTCP(1234,factory)
reactor.run()
使用LineReceiver協議改進的版本:
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()