第十四章

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)
            

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值