python基础教程(第三版)学习笔记(十四)

第十四章 网络编程
 

鉴于Python提供的网络工具众多,这里只能简要地介绍它的网络功能。 本章首先概述Python标准库中的一些网络模块。然后讨论SocketServer和相关的类,并介绍 地介绍同时处理多个连接的各种方法。最后,简单地说一说Twisted,这是一个使用Python编写网 络程序的框架,功能丰富而成熟。
 

14.1 几个网络模块
 

14.1.1 模块 socket
 

网络编程中的一个基本组件是套接字(socket)。套接字基本上是一个信息通道,两端各有一 个程序。在Python中,大多数网络编程都隐藏了模块socket的基本工作原理,不与套接字直接交互。
 

套接字分为两类:服务器套接字和客户端套接字。创建服务器套接字后,让它等待连接请求 的到来。这样,它将在某个网络地址(由IP地址和端口号组成)处监听,直到客户端套接字建立 连接。随后,客户端和服务器就能通信了。
 

客户端套接字处理起来通常比服务器端套接字容易些,因为服务器必须准备随时处理客户端 连接,还必须处理多个连接;而客户端只需连接,完成任务后再断开连接即可。
 

本章后面将介绍 如何使用SocketServer等类和Twisted框架进行服务器端编程。
 

套接字是模块socket中socket类的实例。函数格式为:
 

socket(family,type[,protocol])


 

实例化套接字时最多可指定三个参数:一个地址族 (family,默认为socket.AF_INET);一个是socket类型是流套接字(typ,默认设置socket.SOCK_STREAM,)还是数据报套接字 (socket.SOCK_DGRAM);一个是协议类型(protocol,使用默认值0就好)。创建普通套接字时,不用提供任何参数。
 

地址族(又称协议族):
 

| Family参数 | 描述 |
| --------------- | ---------------------------------- |
| socket.AF_UNIX | 只能够用于单一的Unix系统进程间通信 |
| socket.AF_INET | 服务器之间网络通信 ipv4 |
| socket.AF_INET6 | IPv6 |
 

socket类型:
 

| Type参数 | 描述 |
| ------------------ | ------------------------------------------------------------ |
| socket.SOCK_STREAM | 流式socket , 当使用TCP时选择此参数 |
| socket.SOCK_DGRAM | 数据报式socket ,当使用UDP时选择此参数 |
| socket.SOCK_RAW | 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头Type参数 |
 

协议类型 :
 

| protocol参数 | 描述 |
| ------------------ | ------------------------------------------------------------ |
| socket.IPPROTO_RAW | 相当于protocol=255,此时socket只能用来发送IP包,而不能接收任何的数据。发送的数据需要自己填充IP包头,并且自己计算校验和 |
| socket.IPPROTO_IP | 相当于protocol=0,此时用于接收任何的IP数据包。其中的校验和和协议分析由程序自己完成 |
 

服务器套接字先调用方法bind,再调用方法listen来监听特定的地址。然后,客户端套接字 就可连接到服务器了,办法是调用方法connect并提供调用方法bind时指定的地址(在服务器端, 可使用函数socket.gethostname获取当前机器的主机名)。这里的地址是一个格式为(host, port) 的元组,其中host是主机名(如www.example.com),而port是端口号(一个整数)。方法listen接 受一个参数——待办任务清单的长度(即最多可有多少个连接在队列中等待接纳,到达这个数量 后将开始拒绝连接)。
 

服务器套接字开始监听后,就可接受客户端连接了,这是使用方法accept来完成的。这个方 法将阻断(等待)到客户端连接到来为止,然后返回一个格式为(client, address)的元组,其中 client是一个客户端套接字,而address是前面解释过的地址。服务器能以其认为合适的方式处 理客户端连接,然后再次调用accept以接着等待新连接到来。这通常是在一个无限循环中完成的。
 

为传输数据,套接字提供了两个方法:send和recv(表示receive)。要发送数据,可调用方 法send并提供一个字符串;要接收数据,可调用recv并指定最多接收多少个字节的数据。如果不 确定该指定什么数字,1024是个不错的选择。
 

以下是两个最简单的客户端程序和服务器程序。


 

```python

#serve.py
import socket
s = socket.socket()
host = socket.gethostname()
port = 1234
s.bind((host, port))
s.listen(5)
c, addr = s.accept()
print('Got connection from', addr)
while True:
c.send(b'Thank you for connecting')
c.close()


```
 

```python

#client.py
import socket
s = socket.socket()
host = socket.gethostname()
port = 1234
s.connect((host, port))
print(s.recv(1024))


```
 

结果:
 

```

C:\Users\xx\AppData\Local\Programs\Python\Python37\python.exe E:/pythonProjects/gui
b'Thank you for connecting'

Process finished with exit code 0



```
 

 14.1.2 模块 urllib 和 urllib2
 

 1、打开远程文件
 

几乎可以像打开本地文件一样打开远程文件,差别是只能使用读取模式,以及使用模块 urllib.request中的函数urlopen,而不是open(或file)。
 

```python

from urllib.request import urlopen
webpage = urlopen('http://www.python.org')
w=webpage.read(200)
print(w)


```
 

结果:
 

```

C:\Users\xx\AppData\Local\Programs\Python\Python37\python.exe E:/pythonProjects/print.py
b'<!doctype html>\n<!--[if lt IE 7]> <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9"> <![endif]-->\n<!--[if IE 7]> <html class="no-js ie7 lt-ie8 lt-ie9"> <![endif]-->\n<!--[if IE 8]> <h'

Process finished with exit code 0



```
 

urlopen返回的类似于文件的对象支持方法close、read、readline和readlines,还支持迭 代等。
 

 2、获取远程文件
 

函数urlopen返回一个类似于文件的对象,可从中读取数据。如果要让urllib替你下载文件, 并将其副本存储在一个本地文件中,可使用urlretrieve。 。这个函数不返回一个类似于文件的对 象,而返回一个格式为(filename, headers)的元组,其中filename是本地文件的名称(由urllib 自动创建),而headers包含一些有关远程文件的信息 。如果要给下载的副本指定文件名, 可通过第二个参数来提供。
 

```python

urlretrieve('http://www.python.org', 'C:\python_webpage.html')


```
 

结果:
 

```

C:\Users\xx\AppData\Local\Programs\Python\Python37\python.exe E:/pythonProjects/print.py
b'<!doctype html>\n<!--[if lt IE 7]> <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9"> <![endif]-->\n<!--[if IE 7]> <html class="no-js ie7 lt-ie8 lt-ie9"> <![endif]-->\n<!--[if IE 8]> <h'
E:\python_webpage.html
Server: nginx
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
x-xss-protection: 1; mode=block
X-Clacks-Overhead: GNU Terry Pratchett
Via: 1.1 varnish
Content-Length: 48842
Accept-Ranges: bytes
Date: Mon, 12 Nov 2018 09:49:11 GMT
Via: 1.1 varnish
Age: 286
Connection: close
X-Served-By: cache-iad2145-IAD, cache-tyo19924-TYO
X-Cache: MISS, HIT
X-Cache-Hits: 0, 635
X-Timer: S1542016152.810209,VS0,VE0
Vary: Cookie
Strict-Transport-Security: max-age=63072000; includeSubDomains



Process finished with exit code 0


```
 


 

要清空这样的临时文件,可调用函数urlcleanup且不提供任何参数, 它将负责替你完成清空工作。
 

14.1.3 其他模块
 

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

| 模 块 | 描 述 |
| ------------------ | ------------------------------ |
| asynchat | 包含补充asyncore的功能 |
| asyncore | 异步套接字处理程序 |
| cgi | 基本的CGI支持 |
| Cookie | Cookie对象操作,主要用于服务器 |
| cookielib | 客户端Cookie支持 |
| email | 电子邮件(包括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服务器 |
| smtpd | SMTP服务器模块 |
| smtplib | SMTP客户端模块 |
| telnetlib | Telnet客户端模块 |
| urlparse | 用于解读URL |
| xmlrpclib | XML-RPC客户端支持 |
 

 14.2 SocketServer 及相关的类
 

编写简单的套接字服务器并不难。然而,如果要创建的并非简单服务器, 还是求助于服务器模块吧。模块SocketServer是标准库提供的服务器框架的基石,这个框架包括 BaseHTTPServer、SimpleHTTPServer、CGIHTTPServer、SimpleXMLRPCServer和DocXMLRPCServer等服 务器,它们在基本服务器的基础上添加了各种功能。要实现本模块,必须定义一个继承于基类BaseRequestHandler的处理程序类,并且在此类中重写handle()方法,这个方法可通过属性self.request来访问客户 端套接字。
 

SocketServer包含4个基本的服务器:TCPServer(支持TCP套接字流)、UDPServer(支持UDP 数据报套接字)以及更难懂的UnixStreamServer和UnixDatagramServer。
 

以下是极简服务器的SocketServer版本 的实力代码:
 

```python

from socketserver import TCPServer, StreamRequestHandler
class Handler(StreamRequestHandler):
def handle(self):
addr = self.request.getpeername()
print('Got connection from', addr)
self.wfile.write(b'Thank you for connecting')
server = TCPServer(('', 1234), Handler)
server.serve_forever()


```
 

客户端代码不变:
 

```python

#client.py
import socket
s = socket.socket()
host = socket.gethostname()
port = 1234
s.connect((host, port))
print(s.recv(1024))


```
 

运行结果:
 

```

C:\Users\xx\AppData\Local\Programs\Python\Python37\python.exe E:/pythonProjects/gui
b'Thank you for connecting'

Process finished with exit code 0



```
 

14.3 多个连接
 

前面讨论的服务器解决方案都是同步的:不能同时处理多个客户端的连接请求。如果连接持 续的时间较长,比如完整的聊天会话,就需要能够同时处理多个连接。
 

处理多个连接的主要方式有三种:分叉(forking)、线程化和异步I/O。通过结合使用 SocketServer中的混合类和服务器类,很容易实现分叉和线程化 。
 

 14.3.1 使用 SocketServer 实现分叉和线程化
 

使用框架SocketServer创建分叉或线程化服务器非常简单,几乎不需要任何解释。请注意,Windows不支持分叉。
 

看示例:
 

1、分叉服务器:
 

```python

#serve.py
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()


```
 

(由于我用的是windows系统懒得安装虚拟环境所以不能演示,清凉解)
 

 2、线程化服务器:
 

```python

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(b'Thank you for connecting')
server = Server(('', 1234), Handler)
server.serve_forever()


```
 

运行结果:
 

```

C:\Users\xx\AppData\Local\Programs\Python\Python37\python.exe E:/pythonProjects/gui
b'Thank you for connecting'

Process finished with exit code 0


```
 

14.3.2 使用 select 和 poll 实现异步 I/O
 

由于windows不支持poll所以此节略去。
 

14.4 Twisted
 

Twisted是由Twisted Matrix Laboratories(http://twistedmatrix.com)开发的,这是一个事件驱 动的Python网络框架,在Twisted中, 你能实现事件处理程序,就像在GUI工具包中一样。实际上,Twisted与多个常用 的GUI工具包(Tk、GTK、Qt和wxWidgets)配合得天衣无缝。本节介绍一些基本概念,并演示如何使用Twisted完成一些简单的网络编程任务。掌握这些基 本概念后,你就可参考Twisted文档 。
 

 14.4.1 下载并安装 Twisted
 

windows下安装,运行cmd,在命令提示符下输入:
 

```dockerfile

pip install twisted[tls]


```
 

即安装完毕。
 

也可以用PyCharm安装,如出现:
 

```

AttributeError: module 'pip' has no attribute 'main'


```
 

错误,就找到PyCharm安装目录下的helpers/packaging_tool.py文件,用以下代码替换其中的do_install和do_uninstall 函数 。
 

```python

def do_install(pkgs):
try:
#import pip
try:
from pip._internal import main
except Exception:
from pip import main
except ImportError:
error_no_pip()
return main(['install'] + pkgs)


def do_uninstall(pkgs):
try:
#import pip
try:
from pip._internal import main
except Exception:
from pip import main
except ImportError:
error_no_pip()
return main(['uninstall', '-y'] + pkgs)


```
 

 14.4.2 编写 Twisted 服务器
 

Twisted采用的是基于事件的方法。要编写简单的服务器,只需实现处理如下情形的事件处理程序: 客户端发起连接,有数据到来,客户端断开连接(以及众多其他的事件)。专用类可在基本类的 基础上定义更细致的事件,如包装“数据到来”事件,收集换行符之前的所有数据再分派“数据 行到来”事件。
 

以下代码清单所示的是以上服务器的Twisted版本。在这个版本中,包含一些设置工作:需要实例化Factory,并 设置其属性protocol,让它知道该使用哪种协议(这里是一个自定义协议)与客户端通信。 接下来,开始监听指定的端口,让工厂通过实例化协议对象来处理连接。为此,调用了模块 reactor中的函数listenTCP。最后,通过调用模块reactor中函数run启动这个服务器。
 

```python

#finger02.py
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()


```
 

模块twisted.protocols.basic包含几个预定义的协议,其中一个就是 LineReceiver。它实现了dataReceived,并在每收到一整行后调用事件处理程序lineReceived。
 

```

C:\Users\xx\AppData\Local\Programs\Python\Python37\python.exe E:/pythonProjects/twisted/finger_02.py
Got connection from ('192.168.3.21', 54041)
('192.168.3.21', 54041) disconnected
Got connection from ('192.168.3.21', 54283)
('192.168.3.21', 54283) disconnected
Got connection from ('192.168.3.21', 54408)
Got connection from ('192.168.3.21', 54432)


```
 

关于twisted更详细的信息请参见:
 

https://twistedmatrix.com/documents/current/core/howto/tutorial/intro.html

猜你喜欢

转载自blog.csdn.net/micorjun/article/details/84066443