14.1 几个网络模块
14.1.1 模块socket
1.套接字:socket基本上作为一个信息通道,两端各有一个程序,这些程序一般通过网络连接连在不同的计算机上,通过套接字向对方发送消息。
2.服务器套接字:创建服务器套接字后,让它等待连接请求的到来,服务器套接字先使用方法bind再调用方法listen来监听特定的地址,然后客户端就可以连接到服务器了,是先调用方法connect并提供调用方法bind时指定的地址,地址是一个格式为(host,port)的元组,host作为主机名,port作为端口号,方法listen接受一个意为待办任务清单长度的参数,当服务器套接字开始监听后就可以接受客户端的连接,这是使用方法accept实现的,这个方法将阻断等待到客户端连接为止,然后返回格式为(client,address)的元组,client是客户端套接字,然后以此方式循环。服务器套接字必须随时准备处理客户端连接,还必须处理多个连接。
3.客户端套接字:客户端只需连接,完成任务后再断开连接即可 。
4.传输数据:套接字提供方法send和recv来传输数据,调用方法send并提供一个字符串来发送数据,调用方法recv并指定最多接受多少个字节的数据来接收数据,如果不确定,1024是个好选择。
#这里讨论的服务器编程形式为同步网络编程。
最简单的服务器
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))
14.1.2 模块urllib和urllib2
对于简单的下载,urllib就够了,如果要实现HTTP身份验证或Cookie,或者是编写拓展来处理自己的协议,urllib2是个好选择。
1.打开远程文件
from urllib.request import urlopen
webpage =urlopen('http://www.python.org')
可以像打开本地文件一样打开远程文件,但是只能用只读模式,当连接到网络时,变量webpage将包含一个类似文件的对象,这个对象与此网页相关联。
在没有联网的情况下,可以用模块urllib,使用file打头来打开本机文件。
urllib返回类似于文件的对象支持的方法,如close,read,readline和readlines,以及迭代。
提取网页中链接About的相对URL的方法
import re
text=webpage.read()
m=re.search(b'<a href="([^"]+)".*?>about</a>',text,re.IGNORECASE)
m.group(1)
'/about/'
#当网页发生变化的时候,需要修改正则表达式
2.获取远程文件
函数urlopen返回一个类似于文件的对象,可从中读取数据,如果要用urllib下载文件,并将其的副本存储在本地文件中可使用urlretrieve,这个函数返回一个(filename,headers)的元组,filename是本地文件的名称,由urllib自动创建。
14.1.3 其他模块
P246
14.2 SocketServer 及相关的类
1.SocketServer包含TCPServer(支持TCP套接字流),UDPServer(支持UDP数据报套接字)以及其他的不常用的两个。
2.使用模块SocketServer编写服务器时,大部分代码都处于请求处理器中,每当服务器收到客户端的连接请求时,都将实例化一个请求处理程序,并对他调用各种处理方法来处理请求 ,还可以从这些请求处理器中派生出子类,从而让服务器调用一组自定义的处理方法,从而让服务器调用一组自定义的处理方法。基本请求处理程序类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 connect from',addr)
self.wfile.write('Thank your for connecting ')
server=TCPServer(('',1234),Handler)
server.serve_forever()
14.3多个连接
处理多个连接的方式主要有分叉,线程化,异步I/O,其中分叉占用的资源较多,且在客户端很多时伸缩性不佳,但只要客户端数量适中,效率很高;而线程化可能会带来很多同步问题。
方式 | 定义 |
---|---|
分叉 | 对进程(运行的程序)进行分叉时,基本上是进行复制它,这样得到的两个进程都将从当前位置开始继续往下执行,并且每个进程都有自己的内存副本,原来的进程为父进程,复制的进程为子进程,子进程执行不同的操作 |
线程 | 线程是轻量级进程(子进程),都位于一个进程中并共享内存 ,这减少了占用的资源,但也带来了一些缺点,由于线程共享内存,因此必须确保他们不会彼此干扰或同时修改数据,否则将引发混乱 |
14.3.1 使用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 your 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 connect from',addr)
self.wfile.write('Thank your for connecting ')
server=Server(('',1234),Handler)
server.serve_forever()
14.3.2使用select和poll实现异步I/O
当服务器与客户端通信的时候,来自客户端的数据可能时断时续,使用框架asyncore/asynchat和Twisted采取的方法是只处理当前正在通信的客户端,甚至无需不断监听,只需监听后将它们加入队列即可。这种功能的基石是函数select或poll,其中poll的伸缩性更好,但只有UNIX系统支持。
函数select接受三个必不可少的参数,和一个可选参数,前三个参数为序列,第四个参数为超时时间单位是秒,这三个序列分别表示需要输入和输出以及发生异常的连接,如果没有指定超时的时间,select将等待到有文件描述符准备就绪,如果指定了第四个参数,select将最多等待指定的秒数,如果超时时间为0,select将不断的轮询,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)