Python学习笔记_第十四章:网络编程

Python的网络编程解决方案:

  • Python内有很多针对常见网络协议的库
  • 用Python很容易写出处理各种协议格式的代码(Python非常擅长于处理字节流的各种模式)
    如果想了解更多Python中的网络编程,推荐:Jphn Goerzen 的 The Foundations of Python Network Programming

少数几个网络设计模块

socket模块

网络编程最基本的就是套接字(socket),它可以说是两个端点程序之间的“信息通道”,Python大部分模块隐藏了套接字的基本细节,不直接和套接字交互。
套接字分:服务器套接字、客户端套接字
可以通过实例化socket模块的socket类来获得一个套接字,需要3个参数,第一个是地址簇:默认为socket.AF_INET,第二个是流:默认为socket.SOCK_STREAM,第三个是使用的协议:默认为0,对于一个普通的套接字不需要任何参数。
获得套接字后可以通过bind方法绑定到一个地址,再调用listen方法去监听,listen方法的唯一参数为服务器未处理连接的长度。
服务器开始监听后就可以通过使用accept方法来接受客户端的连接,这个方法是阻塞的,直到有客户端连接,连接后其返回值为元祖(client, address),服务器处理完一个连接后,再次调用accept来等待下一个连接。
以上描述的服务器编程为阻塞或同步网络编程
套接字对象有两个方法:send和recv
在Linux或UNIX系统中,需要有系统管理员权限才能使用1024以下的端口。这些地域1024的端口被标准服务占用,如:80用于web
一个小型服务器:

import socket
s = socket.socket()

host = socket.gethostname()
print host
prot = 1234

s.bind((host, prot))

s.listen(5)

while True:
    c, address = s.accept()
    print 'Got connection form', address
    c.send('Thank you for connect')
    print c.recv(1024)
    c.close()

一个小型客户端:

import socket
s = socket.socket()
host = socket.gethostname()
port = 1234
s.connect((host, port))
print s.recv(1024)
s.send('hello server')

在Python库参考文档(http://python.org/doc/lib/module_socket.html)和Gordon McMillan的Socket Programming HOWTO(http://doc.python.org/dev/howto/sockets.html)中找到更多socket模块的内容。

urlib和urllib2模块的内容

在能用的各个网络库中,最强大的就数urlib和urllib2了,通过他们调用网络文件如调本地,几乎可以把任何URL指向的东西作为程序的输入。可以想象下将它们和re模块结合的效果。
这俩的功能相近,但urlib2更好点,如果只是简单的下载urlib就够了,如果需要HTTP验证或cookie,或者要为自己的协议编写扩展,那就urlib2更好了。
1.打开远程文件

>>> from urllib import urlopen
>>> webpage = urlopen('www.baidu.com')
>>> webpage = urlopen('http://www.baidu.com')
>>> webpage
<addinfourl at 88157960L whose fp = <socket._fileobject object at 0x00000000053C2E58>

2.获取远程文件

>>> from urllib import urlretrieve
>>> urlretrieve('http://www.python.org', 'C:\U')
>>> urlretrieve('http://www.python.org', r'C:\Users\hcs\Desktop\python_webpage.html')
('C:\\Users\\hcs\\Desktop\\python_webpage.html', <httplib.HTTPMessage instance at 0x000000000542D708>)

以上,如果不指定保存位置,文件就会保存在零时位置,要清理,可以调用urlcleanup函数。
一些功能
处理读取和下载文件,urllib来提供了些函数操作URL本身:

函数 描述
quote(string[, safe]) 返回一个字符串,其中所有URL特殊字符都被URL友好的字符代替
quote_plus(string[, safe]) 和quote功能差不多,但用+代替空格
unquote(string) 和quote相反
unquote_plus(string) 和unquote_plus相反
urlencode(query[, doseq]) 将映射或者包含两个元素的元组的序列转换为URL格式的字符串,这种字符串可以用于CGI查询

其他模块

标准库中一些和网络相关的模块

模块 描述
asynchat asyncore的增强版(从更高层次处理异步IO的框架)
asyncore ……
cgi 通用网关接口
Cookie Cookie对象操作,主要用于服务器
cookielib 客户端cookie支持
email email消息支持
ftplib FTP客户端模块
gopherlib gopher客户端模块(Internet提供的采用菜单式驱动的信息查询工具,采用C/S)
httplib HTTP客户端模块
imaplib imap4客户端模块
mailbox 读取几种邮箱格式
mailcap 通过mailcap文件访问MIME配置
mhlib 访问MH邮箱
nntplib NNTP客户端模块
poplib pop客户端模块
robotparser 支持解析web服务器的robot文件
SimpleXMLRPCServer 一个简单的XML-RPC服务器
smtpd SMTP服务器端模块
smtplib SMTP客户端模块
telnetlib Telnet客户端模块
urlparse 支持解析URL
xmlrpclib XML-RPC的客户端支持

SocketServer和它的朋友们

SocketServer模块是标准库中很多服务器框架的基础,这些服务器框架包括:BaseHTTPServerSimpleHTTPServerCGIHTTPServerSimpleXMLRPCServerDocXMLRPCServer,所有的这些服务器框架都为基础服务器增加了特定的功能。
SocketServer包括四个基本的类:针对TCP流式套接字的TCPServer;针对UDP数据报套接字的UDPServer;以及针对性不强的UnixStreamServer和UnixDatagramServer。后三个不常用。
前面小型服务器的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')
        print self.rfile.read()
server = TCPServer(('', 1234), handler)
server.serve_forever()

它能和前面小型客户端协同工作
Python参考库(http://python.org/doc/lib/module-SocketServer.html)和John Goerzen的The Foundations of Python Network Programming可以找到关于SocketServer的更多信息。

多个连接

前面的服务器解决方案都是同步的:即一次只能连接一个客户机并处理,为了实现异步可以有三种方法:分叉(forking)、线程(threading)以及异步I/O,分叉在现代UNIX和Linux系统很高效,但是windows不支持分叉。
通过SocketServer服务器使用混入类很容易派生进程和线程。
避免线程和分叉的另外一种方法是转换到Stackless Python(http://stackless.com),一个为了能够在不同的上下文之间快速、方便切换而设计的Python版本。,它支持一个叫做微线程的类线程的并行形式。

使用Socket Server进行分叉和线程处理

使用分叉技术的服务器(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 connecting')
        self.rfile.read()
server = TCPServer(('', 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')
        self.rfile.read()
server = TCPServer(('', 1234), handler)
server.serve_forever()

不知道为啥,两种方式实现的效果一样,都不太正常

带有select和poll的异步I/O

这种处理方式只处理在给定的时间内真正要进行通信的客户端,不需要一直监听,只需要监听一会,然后把它放到其他客户端的后面。这个功能的基础是select,如果poll可用,也可以是它,这两个函数都来自select模块。poll只能在UNIX中使用。
select有三个必选的序列参数,还有一个可选的以秒为单位的超时时间作为第四个参数。三个序列为文件描述符整数或者带有可以产生这样整数的fileno方法的对象,这些都是我们等待连接的对象。
三个序列分别用于:输入、输出、异常情况,select函数的输出为三个序列,分别对应这三种输入参数序列的一个活动子集
序列能包含文件对象(在windows中行不通)或者套接字。
简单的select服务器

import socket, select

s = socket.socket()

addr = socket.gethostname()

s.bind((addr, 1234))
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:
                disconnected = True
            if disconnected:
                print r.getpeername(), 'disconnected'
                inputs.remove(r)
            else:
                print data

poll方法返回的元组(fd, event)中event的类型(select模块中的polling时间常量类型)有:

事件名 描述
POLLIN 读取来自文件描述符的数据
POLLPRI 读取来自文件描述符的紧急数据
POLLOUT 文件描述符已经准备好数据,写入时不会发生阻塞
POLLERR 与文件描述符有关的错误情况
POLLHUP 挂起,连接丢失
POLLNVAL 无效请求,连接没有打开

使用poll代替select的代码(只有在UNIX有用)

import select, socket

s = socket.socket()
addr = socket.gethostname()
port = 1234
s.bind((addr, port))
s.listen(5)
fdmap = {s.fileno() : s}
p = select.poll()
p.register(s)
while True:
    events = p.poll()
    for fd, event in events:
        if fd == s.fileno():
            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)

在Python库参考文档(http://python.org/doc/lib/module-select.html)中可以找到更多关于select和poll的信息。

Twisted

Twisted是一个事件驱动的Python网络框架,它能和几个常见的GUI工具包(Tk, GTK, Qt, wxWidget)协同工作。它相当丰富,支持web服务器、客户机、SSH2、SMTP、POP3、IMAP4、AIM、ICQ、IRC、MSN、Jabber、NNTP和DNS等等。

下载和安装Twisted

windows直接登录网址http://twistedmatrix.com下载Python版本对应的Windows安装程序就好
其他系统:如果有包管理器(APT、RPM、DPKG、Portage、Fink、MacPorts)那么可以用它们直接安装,如果没有用包管理器,则需要下载档案文件,用tar命令解压缩后运行Distutils脚本:python setup.py install

编写Twisted服务器

Twited使用一个事件甚至多个基于事件的方法,要编写基本的服务器就要实现处理比如:新客户端连接、数据到达以及一个客户端断开连接等事件的事件处理程序。下面是个例子:

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()

上面的服务器程序每行之输出了个字符,为了能实现每次输出一行,twisted.protocols.basic模块中包含一个有用的预定义协议,是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 dataReceived(self, line):
        print line,
factory = Factory()
factory.protocol = SimpleLogger
reactor.listenTCP(1234, factory)
reactor.run()

关于twisted,可以在网站http://twistedmatrix.com上找到

猜你喜欢

转载自blog.csdn.net/weixin_34152820/article/details/87160509
今日推荐