【循序漸進學Python】15.網絡編程


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 模塊

urlliburllib2 是Python標准庫中最強的的網絡工作庫。通過這兩個庫所提供的上層接口,使我們可以像讀取本地文件一樣讀取網絡上的文件。而且 urllib2 並不是 urllib 的升級版本(應該是一種補充),二者是不可相互替代的。

通過使用 urlliburlopen 函數可以很容易的打開遠程的文件,如下:

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 函數。標准庫中的 asyncoreasynchat 模塊對它們進行了進一步的包裝,可以從更高層次來處理異步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()

參考資料&進一步閱讀

Python基礎教程(第二版)

Twisted


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM