第14章 网络编程
网络编程是现代编程主题中的一个重要组成部分,而Python在标准库中就已经提供了丰富的网络编程模块,以支持用户进行编写具有各种网络功能的程序或软件。在Python标准库中,支持底层网络编程的是socket模块;针对特定的网络协议进行编程的模块有urllib、http、ftplib、poplib、smtplib、telnetlib、socketserver等,还有ipaddress等工具模块。此外,进行网络编程的还有著名的第三方模块Twisted,用来进行异步网络编程,也是极好的工具,不过此时只支持python2,相信未来也会支持Python3版本的。
14.1 网络编程基础
作为网络程序的开发者,无论开发的是服务器端程序还是客户端程序,都需要扎实的计算机网络方面的基础知识,这样才能在网络编程时游刃有余,此处仅对计算机网络相关知识作出必要的介绍,详细的内容应参考相关资料。
14.1.1 什么是网络
计算机网络是一些相互连接的自主计算机或设备的集合,它是计算机技术和通信技术相结合的产物。这些计算机之间的连接可以使用任何一种能够通信的介质,比如网线、光纤、微波、红外线,甚至通信卫星。而自主计算机或设备可以是如大型计算机、微型计算机、笔记本电脑、平板电脑、手机、路由器、调制解调器(俗称猫)等。
计算机网络根据其覆盖的地域范围的大小可分为局域网、城域网和广域网。而其中局域网(LAN)的作用范围最小,一般为一个单位、一幢楼、一间办公室或一个家庭的网络,有的甚至只有两台计算机;城域网(MAN)的作用范围一般是一个城市或几个街区等,可以为一个或几个单位拥有,也可以是公用的,能将多个局域网互联。广域网(WAN)的作用范围更大,可以是一个省、一个国家甚至是全球。现在我们看到的网络大多是相互连接在一起的,就形成了互联网络,即互联网(Internet)。
14.1.2 网络协议
网络协议是网络中进行数据交换与传输所需要的规则、标准或约定,主要由语法(数据与信息的结构形式)、语义和同步(事件的实现顺序)三个要素组成。
世界上最先提出的协议理论模型是由国际标准化组织(ISO)提出的开放系统互联基本参考模型(OSI),它采用的是七层协议的体系结构。虽然OSI清晰完整,但终因复杂又不实用而没有得到实用。另一方面,使用了简化的OSI的TCP/IP协议却得到了非常广泛的应用,它是一个四层的体系结构,包括应用层、运(传)输层、网际(络)层和网络接口层。
如图14.1所示,TCP/IP协议其实是一个协议簇,不仅包括TCP和IP协议,还包括UDP、FTP、HTTP、SMTP等,此外还包括一些图中没有显示的ICMP、ARP、RARP等协议。
这种分层的协议结构还表示出,上层协议需要传输的数据,应该交给它紧邻的下层。而应用层和传输层分别有两个以上协议,所以对于应用层来说,不同的协议数据可以通过传输层的不同协议来传输。例如同是文件传输协议,FTP协议在传送数据时就使用下层的TCP协议,而TFTP协议使用下层的UDP协议进行数据传输。
注意在实际的网络程序开发中,必须要对其使用的协议有相关的理解。
14.1.3 地址与端口
在互联网上同时连接的计算机数量庞大,要想与某一台计算机进行数据传输,就必须要先找到对方计算机。在TCP/IP协议中的网络层的IP协议提供了网络上的计算机地址系统,即IP地址。
目前广泛使用的IP地址是IPv4版本的地址,使用32位二进制代码表示的网络地址,为了方便人们使用,将32位二进制代码划分为4个8位的二进制代码,并将其转换为十进制数,中间用点来分开,称为点分十进制表示法。如图14.2所示,实际的IP地址
01111101000011011001101000110101用点分十进制表示法则为在同一台计算机中可以同时运行多个网络程序,协议又如何区分这不同的网络程序所传输的数据呢?这就是端口的使用。TCP/IP协议规定端口值范围为0~65535,共65536个。而在同一台计算机上TCP和UDP都有自己的端口范围,其范围也相同且并不冲突。
端口在TCP/IP协议中共分为三类:
在同一台计算机中可以同时运行多个网络程序,协议又如何区分这不同的网络程序所传输的数据呢?这就是端比如常用的FTP协议端口号为21,HTTP常用端口为80等。而程序员开发期间可以根据自己开发的应用和所用计算机的情况来使用端口。如开发WEB服务器,也可以使用80端口(计算机没有使用80端口服务),但一般在开发阶段会使用上述分类中的注册端口,待完成后部署时,改为80端口也可以。
因此,在编写服务器端程序时,应该指定服务的IP地址与端口;而在编写客户端程序时,应该指定要连接的服务器的IP地址与端口。而在开发阶段,由于调试程序可能只在开发者的一台计算机上同时运行服务器与客户端,这时可以指定一个特殊的IP地址,即127.0.0.1(回环地址)或直接用字符串localhost来代表本机,当程序实际部署时,应根据所部署的计算机来指定IP地址与端口即可。
注意 要学好网络编程,必须搞清楚IP地址与端口概念及应用。
14.2 套接字的使用
TCP/IP协议中的TCP和UDP协议都通过一种名为套接字(socket)来实现网络功能。套接字是一种类文件对象,它使程序能接受客户端的连接或建立对客户端的连接,用以发送和接收数据。不论是客户端程序还是服务器端程序,为了进行网络通信,都要创建套接字对象。
14.2.1 用socket建立服务器端程序
在Python标准库中,使用socket模块中提供的socket对象,就可以在计算机网络中建立服务器与客户端,并且能够进行通信。服务器端需要建立一个socket对象,并等待客户端的连接。客户端使用socket对象与服务器端进行连接,一旦连接成功,客户端和服务器端就可以进行通信了。
socket模块中的socket对象是socket网络编程的基础对象,其初始化原型如下:
socket( family, type, proto)
其参数含义如下:
- family:地址族,可选参数。默认为AF_INET(IPv4),也可以是AF_INET6或AF_UNIX;
- type:socket类型,可选参数。默认为SOCK_STREAM(TCP协议),可用SOCKET_DGRAM(UDP协议);
- proto:协议类型,可选参数。默认为0。
注意 以上只列出常用参数,其他参数可以参考相关资料。
作为服务器端的socket对象主要应用以下常用方法:
1.bind(address)
其参数address是由IP地址和端口组成的元组,例如“(‘127.0.0.1’,1051)”。如果IP地址为空,则表示本机。它的作用是使
socket和服务器服务地址相关联。
2.listen(backlog)
参数backlog指定在拒绝连接之前,操作系统允许它的最大挂起连接数量。最小值为0(如果用户使用了更小的值,则会自动被置为0),大部分程序最多设置为5就足够了。该方法将socket设置为服务器模式,之后就可以调用以下的accept()方法等待客户端的连接。
3.accept()
它会等待进入的连接,并返回一个由新建的与客户端的socket连接和客户端地址组成的元组,而客户的地址也是一个由客户端IP地址和端口组成的元组。
4.close()
显而易见,这个方法的作用就是关闭该socket,停止本程序与服务器或客户端的连接。
5.recv(buffersize[,flag])
用于接收远程连接发来的信息,并返回该信息。buffersize可以设定缓冲区的大小。
6.send(data[,flags])
用于向连接的远端发送信息,data应该是bytes类型的数据。其返回值为已传送的字节数。而建立服务器端的socket就要依次使用这几个方法,其基本顺序如图14.3所示。
14.2.2 用socket建立客户端程序
相比用socket建立服务器端而言,建立客户端程序要简单得多。当然还是需要创建一个socket的实例,而后调用这个socket实例的connect()方法来连接服务器端即可。这个方法原型如下:
connect( address)
参数address通常也是一个元组(由一个主机名/IP地址,端口构成),当然要连接本地计算机的话,主机名可直接使用'localhost’,它用于将socket连接到远程以address为地址的计算机。用socket建立客户端程序的基本流程如图14.4所示。
14.2.3 用socket建立基于UDP协议的
服务器与客户端程序
通过使用socket应用传输层的UDP协议建立服务器与客户程序,从步骤上来看,要比使用TCP协议还要简单一点。发送和接收数据使用socket对象的主要方法如下:
recvfrom (bufsize[, flags]) # bufsize用来指定缓冲区大小
主要用来从socket接收数据,该方法用于连接UDP协议
sendto (bytes, address)
参数bytes是要发送的数据,address是发送信息的目标地址,仍然是由目录IP地址和端口构成的元组。主要用来通过UDP协议将数据发送到指定的服务器端。用socket建立基于UDP协议的服务器流程如图14.6所示。
注意 使用UDP协议进行网络编程和使用TCP协议进行网络编程的区别与联系,以及它们的应用场合也是不同的。
14.2.4 用socketserver模块建立服务器
虽然使用socket模块可以创建服务器,但是程序员要对包括网络连接等进行管理和编程。为了更加方便地创建网络服务器,Python标准库中提供了一个创建网络服务器的框架——socketserver。
socketserver框架将处理请求划分为两个部分,分别对应服务器类和请求处理类。服务器类处理通信问题,请求处理类处理数据交换或传送。这样,更加容易进行网络编程和程序的扩展。同时,该模块还支持快速的多线程或多进程的服务器编程。
socketserver模块中使用的服务器类主要有TCPServer、UDPServer、ThreadingTCPServer、ThreadingUDPServer、ForkingTCPServer、ForkingUDPServer等。其中有TCP字符的就是使用TCP协议的服务器类,有UDP字符的就是使用UDP协议的服务器类,有Threading字符的是多线程服务器类,有Forking字符的是多进程服务器类。要创建不同类型的服务器程序,只需继承其中之一或直接实例化,然后调用服务器类方法serve_forever()即可。这些服务器的构造方法参数主要有:
server_address # 由IP地址和端口构成的元组
RequestHandlerClass # 处理器类,供服务器类调用处理数据
socketserver模块中使用的处理器类主要有StreamRequestHandler(基于TCP协议的)和DatagramRequestHandler(基于UDP协议的)。只要继承其中之一,就可以自定义一个处理器类。通过覆盖以下三个方法,实现自定义:
- setup() 为请求准备请求处理器(请求处理的初始化工作);
- handler() 完成具体的请求处理工作(解析请求、处理数据、发出响应);
- finish() 清理请求处理器相关数据。一般地说,自定义一个简单的请求处理器,只需覆盖handler()方法即可。
注意 实例中也可以使用self.rfile.readline()和self.wfile.write(data)这两个类文件对象进行收发数据。但客户端每次发送数据要有行结束符。
14.3 urllib与http包使用
Python标准库中的socket模块主要应用于底层网络协议,编写程序要从底层开始构建,要自行处理相关协议比较麻烦。其实对于大多数程序员来说,网络编程都是针对应用协议进行的。比如本节所述的urllib与http包。
14.3.1 urllib和http包简介
urllib、http主要是面向HTTP协议的,而网络上的网站应用都是基于HTTP协议,因此使用它们编程可以轻松访问网站。其中urllib主要用于处理URL(Universal Resource Locators),使用urllib操作URL可以像使用和打开本地文件一样的操作,非常简单而又易上手。http则实现了对HTTP协议的封装,是urllib.request的底层。
1.urllib包简介
urllib包的主要模块有:
urllib.request 用于打开URL网址;
urllib.error 定义了常见的urllib.request会引发的异常;
urllib.parse 用于解析URL;
urllib.robotparser 用于解析robots.txt文件。
urllib.request模块中提供了用于打开一个URL的函数urlopen(),其原型如下:
urlopen (url, data, proxies) 其参数含义如下:
url 要进行操作的URL地址;
data 向URL传递的数据,可选参数;
proxies 使用的代理地址,可选参数。
urlopen将返回一个HTTPResponse实例(类文件对象),可以像操作文件一样使用read、readline、close等方法对URL进行操作。使用urllib.request模块中的urlretrieve方法可以将URL保存为本地文件。urlretrieve方法的原型如下:
urlretrieve (url, filename, reporthook, data) 其参数含义如下:
url 要保存的URL地址;
filename 指定保存的文件名,可选参数;
reporthook 回调函数,可选参数;
data 发送的数据,一般用于POST,可选参数。
在urllib.parase模块中还有一个可以对URL进行编码的函数urlencode(),其原型如下:urlencode (query, doseq)
其参数含义如下:
query 要进行编码的变量和值组成的字典;
doseq 可选参数,若为True则将为元组的值分别编码成“变量=值”的形式。
使用urllib.parse模块中的quote方法和quote_plus方法可以替换字符串中的特殊字符,使其符合URL所要求使用的字符。这两个方法的原型如下:
quote (string, safe)
quote_plus (string, safe)
这两个方法的参数相同,含义如下:
string 要进行替换的字符串;
safe 可选参数,指定不需要替换的字符,默认为“/”。
使用urllib.parse模块中的unquote方法和unquote_plus方法可以将使用quote方法和quote_plus方法替换后的字符还原(在Python2.x中直接由urllib模块进行处理)。这两个方法的原型如下:
unquote(string)
unquote_plus(string)
其参数string就是要进行还原的字符串。
此外,这个包中还提供了一些处理URL所需要的如认证处理类、cookie处理类等一些打开URL所需的基类。
2.http包简介
http包提供了使用HTTP协议的一些功能,其主要模块如下:
http.client 底层的HTTP协议客户端,可以为urllib.request模块所用;
http.server 提供了基于socketserver模块的基本HTTP服务器类;
http.cookies cookies的管理工具;
http.cookiejar 提供了cookies的持久化支持。
在http.client模块中主要包括两个用于客户端类:
HTTPConnection基于HTTP协议的访问客户端;
HTTPSConnection 基于HTTPS协议的访问客户端;
HTTPResponse 基于HTTP协议的服务端回应。
本节主要介绍HTTPConnection与HTTPResponse。
(1) HTTPConnection构造方法的原型为:
HTTPConnection (host, port=None, [timeout, ]source_address=None)
其基本参数意义如下:
host 服务器地址(可以使用www.abc.com:8080模式);
port 用来指定访问的服务器端口,不提供的话则从host提取,否则使用80端口;
timeout 指定超时秒数。
HTTPConnection对象的主要方法如下:
request (method, url, body, headers)
其参数含义如下:
method:发送的操作,一般为“GET”或“POST”;
url:进行操作的URL;
body:发送的数据;
headers:发送的HTTP头。
当向服务器发送请求后,可以使用HTTPConnection对象的getresponse()方法返回一个HTTPResponse对象。使用HTTPConnection对象的close()方法可以关闭同服务器的连接。除了使用request方法以外,还可以依次使用如下的方法向服务器发送请求。
putrequest (request, selector, skip_host, skip_accept_encoding)
putheader (header, argument, …)
endheaders()
send (data)
putrequest方法的参数含义如下:
putheader方法的参数含义如下:
14.3.2 用urllib和http包访问网站
urllib.request是http.client的抽象,要访问网站,可以使用urllib.request.urlopen(),只需要一行代码,非常简单。当然也可以使用http.client.HTTPConnection对象。
14.4 用poplib与smtplib库收发邮件
Python中的poplib模块和smtplib模块提供了对POP3协议和SMTP协议的支持,使用POP3协议可以登录Email服务器收取邮件,使用SMTP协议则可通过Email服务器发送邮件。
14.4.1 用poplib检查邮件
一般的邮箱服务都提供了POP3收取邮件的方式,Outlook等Email客户端就是使用POP3协议收取邮箱中的邮件。使用Python的poplib模块可以编写一个简单地检查邮件的客户端脚本。
2.使用Python检查Email
使用Python检查Email,首先应该知道自己所使用的Email的POP3服务器地址和端口。一般来说,邮箱服务器的地址为pop.主机名.域名,而端口的默认值为110。例如126邮箱的POP3服务器地址为pop.126.com,端口为默认值110。如果连接不上,就要查看网站帮助,获取POP3服务器的地址和端口。
注意:代码中用户名应该写入邮箱的email地址。
14.4.2 用smtplib发送邮件
注意:邮件的主体内容中的中文字符,应当使用encode()进行编码。
14.5 用ftplib访问FTP服务
Python中的ftplib模块提供了用于访问FTP的函数。使用ftplib模块可以在Python脚本中访问FTP,完成文件的上传、下载等操作。
14.5.1 ftplib模块简介
使用ftplib模块中的FTP类,可以创建一个FTP连接对象。其原型如下:
14.5.2 使用Python访问FTP
14.6 小结
本章主要介绍了在Python语言中进行网络编程的基础知识,利用socket建立客户端与服务器(基于TCP协议与UDP协议),利用socketserver模块快速建立服务器,用urllib与http包访问网站、利用poplib和smtplib检查与发送邮件,用ftplib访问FTP服务器。