网络编程
几个网络模块
- 模块socket
# 简单的服务器
import socket
s = socket.socket()
host = socket.gethostname()
port = 1234
s.bind((host, port))
s.listen(5)
while True:
c, addr = s.accept()
print('Got connection from', addr)
c.send('Thank you for connecting')
c.close()
# 简单的客户端
import socket
s = socket.socket()
host = socket.gethostname()
port = 1234
s.connect((host, port))
print(s.recv(1024))
- 模块urllib和urllib2
打开远程文件:urlopen
>>> from urllib.request import urlopen
>>> webpage = urlopen('http://www.python.org')
>>> import re
>>> text = webpage.read()
>>> m = re.search(b'<a href="([^"]+)" .*?>about</a>', text, re.IGNORECASE)
>>> m.group(1)
'/about/'
获取远程文件:urlretrieve
>>> urlretrieve('http://www.python.org', 'C:\\python_webpage.html')
清空这样的临时文件:urlcleanup
- 其他模块
标准库中一些与网络相关的模块
模块 | 描述 |
---|---|
asynchat | 包含补充asyncore的功能(参见第24章) |
asyncore | 异步套接字处理程序(参见第24章) |
cgi | 基本的CGI支持(参见第15章) |
Cookie | Cookie对象操作,主要用于服务器 |
cookielib | 客户端Cookie支持 |
电子邮件(包括MIME)支持 | |
ftplib | FTP客户端模块 |
gopherlib | Gopher客户端模块 |
httplib | HTTP 客户端模块 |
imaplib | IMAP4客户端模块 |
mailbox | 读取多种邮箱格式 |
mailcap | 通过mailcap文件访问MIME配置 |
mhlib | 访问MH邮箱 |
nntplib | NNTP客户端模块(参见第23章) |
poplib | POP客户端模块 |
robotparser | 解析Web服务器robot文件 |
SimpleXMLRPCServer | 一个简单的XML-RPC服务器(参见第27章) |
smtpd | SMTP服务器模块 |
smtplib | SMTP客户端模块 |
telnetlib | Telnet客户端模块 |
urlparse | 用于解读URL |
xmlrpclib | XML-RPC客户端支持(参见第27章) |
SocketServer
- 4个基本服务器:TCPServer(支持TCP套接字流)、UDPServer(支持UDP数据报套接字)以及更难懂的UnixStreamServerUnixDatagramServer。
- 请求处理器:基本请求处理程序类BaseRequestHandler将所有操作都放在一个方法中——服务器调用的方法handle。这个方法可通过属性self.request来访问客户端套接字。可使用StreamRequestHandler类,它包含另外两个属性:self.rfile(用于读取)和self.wfile(用于写入)。
# 基于SocketServer的极简服务器
from socketserver import TCPServer, StreamRequestHandler
class Handler(StreamRequestHandler):
def handle(self):
addr = self.request.getpeername()
print('Got connection from', addr)
self.wfile.write('Thank you for connecting')
server = TCPServer(('', 1234), Handler)
server.serve_forever()
多个连接
- 分叉(forking) 占用的资源较多,且在客户端很多时可伸缩性不佳(但只要客户端数量适中,分叉在现代UNIX和Linux系统中的效率很高。如果系统有多个CPU,效率就更高了)
- 线程化 可能带来同步问题
- 异步I/O
- 使用 SocketServer 实现分叉和线程化
# 分叉服务器
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()
# 线程化服务器
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 和 poll 实现异步 I/O
当服务器与客户端通信时,来自客户端的数据可能时断时续
- 分叉和线程化:一个进程(线程)等待数据时,其他进程(线程)可继续处理其客户端。
- 异步I/O:只处理当前正在通信的客户端。你甚至无需不断监听,只需监听后将客户端加入队列即可。
# 使用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的简单服务器
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)
Twisted
基于事件:要编写简单的服务器,只需实现处理如下情形的事件处理程序:客户端发起连接,有数据到来,客户端断开连接(以及众多其他的事件)。专用类可在基本类的基础上定义更细致的事件,如包装“数据到来”事件,收集换行符之前的所有数据再分派“数据行到来”事件。
# 使用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()
PS:你不能使用事件处理策略来向客户端发送数据。这种工作是使用对象self.transport完成的,它包含一个write方法。这个对象还有一个client属性,其中包含客户端的地址(主机名和端口)。
小结
套接字和模块socket:套接字是让程序(进程)能够通信的信息通道,这种通信可能需要通过网络进行。模块socket让你能够在较低的层面访问客户端套接字和服务器套接字。服
务器套接字在指定的地址处监听客户端连接,而客户端套接字直接连接到服务器。
urllib和urllib2:这些模块让你能够从各种服务器读取和下载数据,为此你只需提供指向数据源的URL即可。模块urllib是一种比较简单的实现,而urllib2功能强大、可扩展
性极强。这两个模块都通过诸如urlopen等函数来完成工作。
框架SocketServer:这个框架位于标准库中,包含一系列同步服务器基类,让你能够轻松地编写服务器。它还支持使用CGI的简单Web(HTTP)服务器。如果要同时处理多个连
接,必须使用支持分叉或线程化的混合类。
select和poll:这两个函数让你能够在一组连接中找出为读取和写入准备就绪的连接。这意味着你能够以循环的方式依次为多个连接提供服务,从而营造出同时处理多个连接的
假象。另外,相比于线程化或分叉,虽然使用这两个函数编写的代码要复杂些,但解决
方案的可伸缩性和效率要高得多。
Twisted:这是Twisted Matrix Laboratories开发的一个框架,功能丰富而复杂,支持大多数主要的网络协议。虽然这个框架很大且其中使用的一些成例看起来宛如天书,但其基本用法简单而直观。框架Twisted也是异步的,因此效率和可伸缩性都非常高。对很多自定
义网络应用程序来说,使用Twisted来开发很可能是最佳的选择。