16 网络编程 - 《Python 核心编程》

?? 引言:客户/服务器架构

?? 套接字:通信终点

?? 面向连接与无连接套接字

?? Python 中的网络编程

?? Socket 模块

?? 套接字对象方法

?? TCP/IP 客户端和服务器

?? UDP/IP 客户端和服务器

?? SocketServer 模块

?? Twisted 框架介绍

?? 相关模块

16.1 介绍

什么是客户/服务器架构?

服务器是一个软件或硬件,用于提供客户需要的“服 务”。

服务器存在的唯一目的就是等待客户的请求,给这些客户服务,然后再等待其它的请求。

客户连上一个(预先已知的)服务器,提出自己的请求,发送必要的数据,然后就 等待服务器的完成请求或说明失败原因的反馈。

服务器不停地处理外来的请求,而客户一次只能提出一个服务的请求,等待结果。然后结束这个事务。

客户之后也可以再提出其它的请求,只是,这个请求会被视为另一个不同的事务了。

        图16-1 Internet 上典型的客户/服务器概念。

硬件的客户/服务器架构

打印(机)服务

文件服务器

软件客户/服务器架构

软件服务器提供的服务主要是程序的运行,数据的发送与接收,合并,升级或其它的程序或 数据的操作。

Web 服务器

数据库服务器

窗口服务器

银行出纳是服务器

图16-2 在此图中的银行出纳“永远不停歇”地为客户提供服务

出纳运行在一个接收请求,处 理请求然后再处理其它请求或等待其它客户的无限循环中。

客户有可能已经排起了长龙,也有可能根本就没有客户。

无论如何,服务器都不会结束。

客户/服务器网络编程

在完成服务之前,服务器必需要先完成一些设置动作。先要创建一个通讯端点,让服务器能“监 听”请求。

服务器在准备好之后,也要通知潜在的客户,让它们知道服务器已经准备好处理服务 了。否则,没有人会提请求的。

比方说,你建立了一个全新的网站。这个网站非常的出色,非常的吸引人,非常的有用,是所有网站中最酷的一个。

但如果你不把网站的网址或者说统一资源定位符(URL)广而告之的话,没有人会知道这个网站的存在的。

所有的客户只要创建一个通讯端点,建立到服务器的连接,然后客户就可以提出请求,请求中,也可以包含必要的数据交互。

一旦请求处理完成,客户收到了 结果,通讯就结束了。

16.2 套接字:通讯端点

什么是套接字?

套接字起源于20 世纪70 年代加利福尼亚大学伯克利分校版本的Unix,即人们所说的BSD Unix,因此,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。

一开始,套接字被设计用在同一台主机上多个应用程序之间的通讯,这也被称进程间通讯,或IPC。

套接字有两种,分别是基于文件型的和基于网络型的。

Python 只支持AF_UNIX,AF_NETLINK,和AF_INET 家族。

套接字地址:主机与端口

一个Internet 地址由网络通讯所必需的主机与端口组成。

合法的端口号范围为0 到65535。

其中,小于1024 的端口号为系统保留端口。

如果你所使用的 是Unix 操作系统,保留的端口号(及其对应的服务/协议和套接字类型)可以通过/etc/services 文件获得。

常用端口号列表可以从下面这个网站获得:

http://www.iana.org/assignments/port-numbers

面向连接与无连接

面向连接

	无论你使用哪一种地址家族。套接字的类型只有两种。一种是面向连接的套接字,即在通讯之
前一定要建立一条连接,就像跟朋友打电话时那样。这种通讯方式也被称为“虚电路”或“流套接
字”。面向连接的通讯方式提供了顺序的,可靠的,不会重复的数据传输,而且也不会被加上数据边
界。这也意味着,每一个要发送的信息,可能会被拆分成多份,每一份都会不多不少地正确到达目
的地。然后被重新按顺序拼装起来,传给正在等待的应用程序。
	实现这种连接的主要协议就是传输控制协议(即TCP)。要创建TCP 套接字就得在创建的时候,
指定套接字类型为SOCK_STREAM。TCP 套接字采用SOCK_STREAM 这个名字,表达了它做为流套接字的
特点。由于这些套接字使用Internet 协议(IP)来查找网络中的主机,这样形成的整个系统,一般
会由这两个协议(TCP 和IP)来提及,即TCP/IP。

面向连接的套接字,即在通讯之 前一定要建立一条连接,就像跟朋友打电话时那样,这种通讯方式也被称为“虚电路”或“流套接字”

面向连接的通讯方式提供了顺序的,可靠的,不会重复的数据传输,而且也不会被加上数据边 界。

实现这种连接的主要协议就是传输控制协议(即TCP)。

无连接

	与虚电路完全相反的是数据报型的无连接套接字。这意味着,无需建立连接就可以进行通讯。
但这时,数据到达的顺序,可靠性及数据不重复性就无法保证了。数据报会保留数据边界,这就表
示,数据不会像面向连接的协议那样被拆分成小块。
	使用数据报来传输数据就像邮政服务一样。邮件和包裹不一定会按它们发送的顺序到达。事实
上,它们还有可能根本到不了!而且,由于网络的复杂性,数据还可能被重复传送。
既然数据报有这么多缺点,为什么还要使用它呢?(一定有什么方面能胜过流套接字的!)由于
	面向连接套接字要提供一些保证,以及要维持虚电路连接,这都是很重的额外负担。数据报没有这
些负担,所以它更“便宜”。通常能提供更好的性能,更适合某些应用场合。
	实现这种连接的主要协议就是用户数据报协议(即UDP)。要创建UDP 套接字就得在创建的时候,
指定套接字类型为SOCK_DGRAM。SOCK_DGRAM 这个名字,也许你已经猜到了,来自于单词“datagram”
(“数据报”)。由于这些套接字使用Internet 协议来查找网络中的主机,这样形成的整个系统,一
般会由这两个协议(UDP 和IP)来提及,即UDP/IP。

无连接套接字与虚电路完全相反的是数据报型的无连接套接字,就像邮政服务一样,这意味着,无需建立连接就可以进行通讯。

无连接的通讯方式的数据到达的顺序,可靠性及数据不重复性就无法保证了,而且数据报会保留数据边界(数据不会像面向连接的协议那样被拆分成小块)。

实现这种连接的主要协议就是用户数据报协议(即UDP)

16.3 Python 中的网络编程

socket()模块函数

要使用socket.socket()函数来创建套接字。其语法如下:

socket(socket_family, socket_type, protocol=0)

socket_family 可以是AF_UNIX 或AF_INET。

socket_type 可以是SOCK_STREAM 或SOCK_DGRAM。

protocol 一般不填,默认值为0。

创建一个TCP/IP 的套接字,你要这样调用socket.socket():

tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

创建一个UDP/IP 的套接字,你要这样:

udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

当我们创建了套接字对象后,所有的交互都将通过对该套接字对象的方法调用进行。

套接字对象(内建)方法

套接字对象的常用函数
	函数						描述
服务器端套接字函数
s.bind()			绑定地址(主机,端口号对)到套接字
s.listen()			开始 TCP 监听
s.accept()			被动接受 TCP 客户的连接,(阻塞式)等待连接的到来
客户端套接字函数
s.connect()			主动初始化 TCP 服务器连接
s.connect_ex()		       connect() 函数的扩展版本, 出错是返回错误码,而不是抛异常
公共用途的套接字函数
s.recv()			接收 TCP 数据
s.send()			发送 TCP 数据
s.sendall()			完整发送 TCP 数据
s.recvfrom()		       接收 UDP 数据
s.sendto()			发送 UDP 数据
s.getpeername()		连接到当前套接字的远端的地址
s.getsockname()		当前套接字的地址
s.getsockopt()		返回指定套接字的参数
s.setsockopt()		设置指定套接字的参数
s.close()		关闭套接字
面向阻塞的套接字方法 (Blocking-Oriented Scoket Methods)
s.setblocking()		设置套接字的阻塞或非阻塞的模式
s.settimeout()		设置阻塞套接字操作的超时时间
s.gettimeout()		得到阻塞套接字操作的超时时间
面向文件的套接字的函数
s.fileno()		套接字的文件描述符
s.makefile()		创建一个与该套接字关联的文件

核心提示:在运行网络应用程序是,最好在不同的电脑上执行服务器和客户端程序		  

核心提示:在运行网络应用程序时,最好在不同的电脑上执行服务器和客户端的程序。

创建一个TCP 服务器

ss = socket()		# 创建服务器套接字
ss.bind()		# 把地址绑定到套接字上
ss.listen()		# 监听连接
inf_loop:		# 服务器无限循环
cs = ss.accept()	# 接受客户连接
comm_loop:		# 通讯循环
cs.recv()/cs.send()	# 对话(接收与发送)
cs.close()		# 关闭客户套接字
ss.close()		# 关闭服务器套接字(可选)	
 1 #!/usr/bin/env python
 2
 3 from socket import *
 4 from time import ctime
 5
 6 HOST = ‘‘ # ‘‘ indicate bind() any effient available address
 7 PORT = 21569
 8 BUFSIZ = 1024 # 1K
 9 ADDR = (HOST, PORT)
10
11 tcpSerSock = socket(AF_INET, SOCK_STREAM)
12 tcpSerSock.bind(ADDR)
13 tcpSerSock.listen(5) # max 5 connection concurrence
14
15 while True:
16         print ‘waiting for connection...‘
17         tcpCliSock, addr = tcpSerSock.accept() # accept() block and wait
18         print ‘...connected from:‘, addr
19
20         while True:
21                 data = tcpCliSock.recv(BUFSIZ)
22                 if not data:
23                         break
24                 tcpCliSock.send(‘[%s] %s‘ % (ctime(), data))
25
26         tcpCliSock.close()
27 tcpSerSock.close() # don‘t forget to close

核心提示:创建线程来处理客户的请求。

创建一个新的线程或进程来完成与客户的通讯是一种非常常用的手段。SocketServer 模块

是一个基于socket 模块的高级别的套接字通讯模块,它支持在新的线程或进程中处理客户的请求。

创建TCP 客户端

cc = socket()			# 创建客户套接字
cc.connect()			# 尝试连接服务器
comm_loop:			# 通讯循环
cs.send()/cs.recv()		# 对话(发送/接收)
cs.close()			# 关闭客户套接字
#!/usr/bin/env python

from socket import *

HOST = ‘localhost‘
PORT = 21569 # should correspond to the server socket
BUFSIZ = 1024 # 1K
ADDR = (HOST, PORT)

tcpCliSock = socket(AF_INET, SOCK_STREAM)
tcpCliSock.connect(ADDR)

while True:
    data = raw_input(‘>‘)
    if not data:
        break
    tcpCliSock.send(data)
    data = tcpCliSock.recv(BUFSIZ)
    if not data:
        break
    print data

tcpCliSock.close()

执行TCP 服务器和客户端

创建一个UDP 服务器

ss = socket()		# 创建服务器套接字
ss.bind()			# 把地址绑定到套接字上
ss.listen()			# 监听连接
inf_loop:			# 服务器无限循环
cs.recvfrom()/cs.sendfrom()	# 对话(接收与发送)
ss.close()			# 关闭服务器套接字(可选)	
 1 #!/usr/bin/env python
 2
 3 from socket import *
 4 from time import ctime
 5
 6 HOST = ‘‘
 7 PORT = 21567
 8 BUFSIZ = 1024
 9 ADDR = (HOST, PORT)
10
11 udpSerSock = socket(AF_INET, SOCK_DGRAM)
12 udpSerSock.bind(ADDR)
13
14 while True:
15     print ‘waiting for message...‘
16     data, addr = udpSerSock.recvfrom(BUFSIZ)
17     udpSerSock.sendto(‘[%s] %s‘ % (18         ctime(), data), addr)
19
20 udpSerSock.close()

创建UDP 客户端

cc = socket()			# 创建客户套接字
comm_loop:				# 通讯循环
cs.sendto()/cs.recvfrom()		# 对话(发送/接收)
cs.close()				# 关闭客户套接字
 1 #!/usr/bin/env python
 2
 3 from socket import *
 4
 5 HOST = ‘localhost‘
 6 PORT = 21567
 7 BUFSIZ = 1024
 8 ADDR = (HOST, PORT)
 9
10 udpCliSock = socket(AF_INET, SOCK_DGRAM)
11
12 while True:
13     data = raw_input(‘>‘)
14     if not data:
15         break
16     udpCliSock.sendto(data, ADDR)
17     data, ADDR = udpCliSock.recvfrom(BUFSIZ)
18     if not data:
19         break
20     print data, ADDR
21 udpCliSock.close()

执行UDP 服务器和客户端

套接字模块属性

	socket 模块属性
	属性名字				描述
数据属性
AF_UNIX, AF_INET, AF_INET6	Python 支持的套接字家族
SO_STREAM, SO_DGRAM		套接字类型(TCP = 流,UDP = 数据报)
has_ipv6			表示是否支持IPv6 的标志变量
异常
error					套接字相关错误
herror					主机和地址相关的错误
gaierror				地址相关的错误
timeout					超时
函数
socket()	用指定的地址家族,套接字类型和协议类型(可选)创建一个套接字对象
socketpair()	用指定的地址家族,套接字类型和协议类型(可选)创建一个套接字对象
fromfd		用一个已经打开的文件描述符创建一个套接字对象
数据属性
ssl()		在套接字初始化一个安全套接字层(SSL)。不做证书验证。
getaddrinfo()	得到地址信息
getfqdn()	返回完整的域的名字
gethostname()	得到当前主机名
gethostbyname()	由主机名得到对应的ip 地址
gethostname_ex()	gethostname()的扩展版本,返回主机名,主机所以的别名和IP 地址列表
gethostbyaddr()		由IP 地址得到DNS 信息,返回一个类型gethostbyname_ex()的3元组
getprotobyname()	由协议名(如‘TCP’)得到对应的号码
getservbyname()/getservbyport()		由服务名得到对应的端口号或相反
ntohl()/ntohs()	         把一个整数由网络字节序转为主机字节序
htonl()/htons()	         把一个整数由主机字节序转为网络字节序
inet_aton()/inet_ntoa()	  把IP 地址转为32位整数,以及反向函数。(仅对IPv4地址有效)
inet_pton()/inet_ntop()	  把IP 地址转为二进制格式以及反向函数。(仅对IPv4地址有效)
getdefaulttimeout()/setdefaulttimeout()	得到/设置默认的套接字超时时间,单位秒(浮点数)

16.4 *SocketServer 模块

SocketServer 是标准库中一个高级别的模块。用于简化网络客户与服务器的实现。

为了要隐藏实现的细节。我们现在写程序时会使用类,这是与之前代码的另一个不同。用面向 对象的方法可以帮助我们更好的组织数据与逻辑功能。

	SocketServer 模块的类
	类						描述
BaseServer			包含服务器的核心功能与混合(mix-in)类的钩子功能。这个类用于
	派生,不要直接生成
TCPServer/UDPServer						基本的网络同步 TCP/UDP 服务器
UnixStreamServer/UnixDatagramServer		基本的基于文件同步的 TCP/UDP 服务器
ForkingMixIn/ThreadingMixIn		实现了核心的进程化或线程化的功能,用于
	与服务器类进行混合(mix-in),已提供一些异步特性。不要直接生成这个类的对象
ForkingTCPServer/ForkingUDPServer	ForkingMixIn 和 TCPServer/UDPServer 的组合
ThreadingTCPServer/ThreadingUDPServer	ThreadingMixIn 和 TCPServer/UDPServer 的组合
BaseRequestHandler		包含处理服务请求的核心功能。只用于派生新的类,不要直接
	生成这个类的对象,可以考虑使用 StreamRequestHandler 或 DatagramRequestHandler
StreamRequestHandler/DatagramRequestHandler	TCP/UDP 	服务器的请求处理类的一个实现

创建一个SocketServerTCP 服务器

 1 #!/usr/bin/env python
 2
 3 from SocketServer import (TCPServer as TCP, 4         StreamRequestHandler as SRH)
 5 from time import ctime
 6
 7 HOST = ‘‘
 8 PORT = 21567
 9 ADDR = (HOST, PORT)
10
11 class MyRequestHandler(SRH):
12     def handle(self):
13         print ‘...connected from:‘, self.client_address
14         self.wfile.write(‘[%s] %s‘ % (ctime(),15                 self.rfile.readline())
16
17 tcpServ = TCP(ADDR, MyRequestHandler)
18 print ‘waiting for connection...‘
19 tcpServ.serve_forever()

创建SocketServerTCP 客户端

 1 #!/usr/bin/env python
 2
 3 from socket import *
 4
 5 HOST = ‘localhost‘
 6 PORT = 21567
 7 BUFSIZ = 1024
 8 ADDR = (HOST, PORT)
 9
10 tcpCliSock = socket(AF_INET, SOCK_STREAM)
11 tcpCliSock.connect(ADDR)
12 while True:
13     data = raw_input(‘>‘)
14     if not data:
15         break
16     tcpCliSock.send(‘%s\r\n‘ % data)
17     data = tcpCliSock.recv(BUFSIZ)
18     if not data:
19         break
20     print data.strip()
21 tcpCliSock.close()

16.5 Twisted 框架介绍

Twisted 是一个完全事件驱动的网络框架。它允许你使用和开发完全异步的网络应用程序和协议。

在写本书的时候,它还不是Python 标准库的一部分,要使用它,你必需另外下载并安装它。它为你

创建一个完整系统提供了很大的帮助。系统中可以有:网络协议,线程, 安全和认证,聊天/即时通讯,

数据库管理,关系数据库集成,网页/互联网,电子邮件,命令行参数,图形界面集成等。

16.6 相关模块

网络/套接字编程相关模块
	模块				描述
socket				底层网络接口。
asyncore/asynchat	为能异步处理客户请求的网络应用程序提供底层功能。
select 				在单线程网路服务器中,管理多个套接字连接。
SocketServer		包含了网络应用服务器所需要的高级别模块。提供了完整的进程和线程的版本。
时间: 2024-11-12 06:31:59

16 网络编程 - 《Python 核心编程》的相关文章

python核心编程中网络爬虫的例子

1 #!/usr/bin/env python 2 3 import cStringIO # 4 import formatter # 5 from htmllib import HTMLParser # We use various classes in these modules for parsing HTML. 6 import httplib # We only need an exception from this module 7 import os # This provides

python核心编程--笔记

python核心编程--笔记 的解释器options: 1.1 –d   提供调试输出 1.2 –O   生成优化的字节码(生成.pyo文件) 1.3 –S   不导入site模块以在启动时查找python路径 1.4 –v   冗余输出(导入语句详细追踪) 1.5 –m mod 将一个模块以脚本形式运行 1.6 –Q opt 除法选项(参阅文档) 1.7 –c cmd 运行以命令行字符串心事提交的python脚本 1.8 file   以给定的文件运行python脚本 2 _在解释器中表示最后

Python核心编程 第3版 中文版pdf

[下载地址] <Python核心编程(第3版)>是经典畅销图书<Python核心编程(第二版)>的全新升级版本,总共分为3部分.第1部分为讲解了Python的一些通用应用,包括正则表达式.网络编程.Internet客户端编程.多线程编程.GUI编程.数据库编程.Microsoft Office编程.扩展Python等内容.第2部分讲解了与Web开发相关的主题,包括Web客户端和服务器.CGI和WSGI相关的Web编程.Django Web框架.云计算.高级Web服务.第3部分则为一

《Python核心编程》 第五章 数字 - 课后习题

课后习题  5-1 整形. 讲讲 Python 普通整型和长整型的区别. 答:普通整型是绝大多数现代系统都能识别的. Python的长整型类型能表达的数值仅仅与你机器支持的(虚拟)内存大小有关. 5-2 运算符 (a) 写一个函数,计算并返回两个数的乘积 (b) 写一段代码调用这个函数,并显示它的结果 答: def pro(a,b): p = a*b return p a = int(raw_input("a=")) b = int(raw_input("b="))

Python核心编程(第二版) 第六章习题答案

6–1.字符串.string 模块中是否有一种字符串方法或者函数可以帮我鉴定一下一个字符串是否是另一个大字符串的一部分? 答:有,string.find(str,beg,end) 6–2.字符串标识符.修改例 6-1 的 idcheck.py 脚本,使之可以检测长度为一的标识符,并且可以识别 Python 关键字,对后一个要求,你可以使用 keyword 模块(特别是 keyword.kelist)来帮你. 1 #!/usr/bin/python 2 3 import string 4 impo

Python核心编程这本书的一些错误

<Python核心编程第二版>这本书比<Python基础教程第二版修订版>详细很多,丰富了很多细节,虽然它是一本经典的入门书,但我发现还是存在一些明显的错误.在面向对象编程这一章,有两个错误 1).它说任何类都有一些内置的特殊的类属性(即程序员不在类中定义也会存在),见截图 2).它说__new__方法比__init__方法更像是类的构造器.见截图: 下面进行测试: 1 #encoding:utf-8 2 class MyClass(): 3 def doPrint(self):

学习《Python核心编程》做一下知识点提要,方便复习(一)

学习<Python核心编程>做一下知识点提要,方便复习. 计算机语言的本质是什么? a-z.A-Z.符号.数字等等组合成符合语法的字符串.供编译器.解释器翻译. 字母组合后产生各种变化拿python来说就是.keyword.数值,类,函数,运算符,操作符...... 1.变量不用声明,类型不固定 2.True,False = False,True读出了什么?True是个bool()类实例 3.类型工厂函数就是python2.2后把int() bool()等对数据类型操作的函数分装成对象 类 了

Python核心编程(第二版) 第五章习题答案

5-1.整型.讲讲Python普通整型和长整型的区别. 答:Python 的标准整数类型是最通用的数字类型.在大多数 32 位机器上,标准整数类型的取值范围是-2**31到 2**31-1,也就是-2,147,483,648 到 2,147,483,647.如果在 64 位机器上使用 64 位编译器编译 Python,那么在这个系统上的整数将是 64 位. Python 的长整数类型能表达的数值仅仅与你的机器支持的(虚拟)内存大小有关. 5-2.操作符.(a)写一个函数,计算并返回两个数的乘积.

Python核心编程第五章习题

Python核心编程-第五章-习题 5.1  整形,讲讲Python普通整形与长整形的区别? Python的标准整形类型是最通用的数字类型.在大多数32位机器上,标准整形类型的取值范围是-2**32-2**32 - 1. Python的长整型类型能表达的数值仅仅与你的机器支持的(虚拟)内存大小有关,换句话说,Python能轻松表达很大的整数. 长整型类型是标准整形类型的超集,当程序需要使用比标准整形更大的整型时,可以使用长整型类型,在整型值后面添加L,表示这个为长整型,3.0版本已经统一称为为整

Python核心编程(第二版) 第二章习题答案 未完待续

2-2.程序输出.阅读下面的Python脚本.#!/usr/bin/env python1 + 2 * 4(a)你认为这段脚本是用来做什么的?(b)你认为这段脚本会输出什么?(c)输入以上代码,并保存为脚本,然后运行它,它所做的与你的预期一样吗?为什么一样/不一样?(d)这段代码单独执行和在交互解释器中执行有何不同?试一下,然后写出结果.(e)如何改进这个脚本,以便它能和你想象的一样工作?答:(a)这段脚本是用来计算表达式的值(b)脚本会输出9(c)保存为脚本,运行后没有输出.和自己预期不一样.