python学习笔记(十 三)、网络编程
博客已迁移到CSDN《https://blog.csdn.net/qq_33375499》
最近心情有点儿浮躁,难以静下心来
Python提供了强大的网络编程支持,很多库实现了常见的网络协议以及基于这些协议的抽象层,让你能够专注于程序的逻辑,而无需关心通过线路来传输比特的问题。
1 几个网络模块
1.1 模块socket
网络编程中的一个基本组件是套接字(socket)。套接字基本上是一个信息通道,两端各有一个程序。这些程序可能位于(通过网络相连接的)不同的计算机上,通过套接字向对方发送消息。在Python中,大多数网络编程都隐藏了模块socket的基本工作原理,不与套接字直接交互。
套接字分为两类:服务端套接字和客户端套接字。创建服务端套接字后,让它等待连接请求的到来。这样,它将在某个网络地址(由IP地址和端口号组成)处监听,知道客户端套接字建立连接,还必须处理多个连接;而客户端套接字只需连接,完成任务后再断开连接即可。
套接字是模块socket中socket类的实例。实例化套接字时最多可指定三个参数:一个地址族(默认为socket.AF_INET);是流套接字(socket.SOCK_STREAM,默认设置)还是数据报套接字(socket.SOCK_DGRAM);协议(使用默认值0就好)。
服务器套接字先调用方法bind,在调研方法listen来监听特定的地址。然后,客户端套接字通过调用方法connect并提供bind时指定的地址来连接服务端。这里的地址是一个格式为(host, port)的元祖,其中host是主机名,port是端口号。方法listen接收一个参数——代办任务清单的长度(即最多可有多少个连接在队列中等待接纳,到达这个数量后将开始拒绝连接)。
服务端套接字开始监听后,就可接收客户端连接,使用方法accept来等待连接。这个方法将阻断(等待)到客户端连接到来为止(有点类似与yield关键字),然后返回一个格式为(client, address)的元组,其中client为客户端套接字,而address为地址。服务端能以其认为合适的方式处理客户端连接,然后再次调用accept以等待新连接到来。
为传输数据,套接字提供了两个方法:send-发送和recv-接收(表示receive),这两个方法都是接收或发送字节流数据。
简单的服务器:
import socket #创建套接字
s = socket.socket() #获取主机名
host = socket.gethostname()
#端口号
port = 8080
address = (host, port)
#创建连接
s.bind(address) #监听:最大连接数为5
s.listen(5)
while True:
#等待连接
client, addr = s.accept()
#接收消息
str = bytes.decode(client.reve(1024))
#发送消息
client.send(str.encode('我是服务端!','utf-8'))
简单的客服端:
import socket
s = socket.socket() #连接服务端地址
host = socket.gethostname()
port = 8080
address = (host, port)
#连接
s.connect(address)
#接收消息
str = bytes.decode(s.reve(1024))
#发送消息
s.sent(str.encode('我是客户端!','utf-8'))
1.2 模块urllib和urllib2
模块urllib和urllib2,由名字可知,它们让你能够通过网络访问文件,就像这些文件位于你的计算机中一样。只需一个简单的函数调用,就几乎可将统一资源定位符(URL)可指向的任何动作作为程序的输入。这两个模块一般用于下载网页、从中提取信息自动生成研究报告等。对于简单的操作,urllib绰绰有余,如果要实现HTTP身份验证或Cookie,或编写扩展来处理自己的协议,urllib2可能是更好的选择。
1.2.1 打开远程文件
使用模块urllib.request中的函数urlopen来打开远程文件,只能进行读取操作。如:
from urllib.request import urlopen
page = urlopen('www.baidu.com')
如果连接到网络,变量page将包含一个类似于文件的对象,这个对象与你所连接的地址相关联。urlopen返回类似于文件的对象支持方法clost、read、readline和readlines,还支持迭代等。
1.2.2 获取远程文件
函数urlopen返回一个类似于文件的对象,可从中读取数据。如果要让urllib替你下载文件,并将其副本存储在本地,可使用urllib.request中的函数urlretrieve。这个函数返回一个格式为(filename, headers)的元祖,其中filename是本地文件的名称(由urllib自动创建),而headers包含一些有关远程文件的信息。如果要给下载的副本指定文件名,可通过函数urlretrieve的第二个参数来指定。
函数格式为: urlretrieve(url, filename=None, reporthook=None, data=None),函数urlretrieve将url中的内容存储在filename中,如果filename没有指定,下载的副本将放在某个临时位置,可使用函数open来打开。但使用完毕后,你可能想将其删除,以免暂用磁盘空间,可调用函数urlcleanup来替你完成清除工作。
1.2.3 其他模块
Python标准库提供了一些与网络相关的模块,如下(只列举了一些常用的):
cgi 基本的CGI文件
asyncore 异步套接字处理程序
asynchat 包含补充asyncore的功能
Cookie Cookie对象操作,主要用于服务器
cookielib 客户端Cookie支持
email 电子邮件支持
ftplib FTP客户端模块
httplib HTTP客户端模块
mailbox 读取多种邮箱格式
urlparse 用于解读URL
2 SocketServer及相关的类
模块SocketServer是标准库提供的服务器框架的基石,这个框架包括BaseHTTPServer、SimpleHTTPServer、CGIHTTPServer、SimpleXMLRPCServer和DocXMLRPCServer等服务器,它们在基本服务器的基础上添加了各种功能。
SocketServer包含4个基本的服务器:TCPServer(支持TCP套接字流)、UDPServer(支持UDP套接字流)、UnixStreamServer和UnixDatagramServer。后面3个不常用。
使用模块SocketServer编写服务器时,大部分代码都位于请求处理中。每当服务器收到客户端的连接请求时,都将实例化一个请求处理程序,并对其调用各种处理方法来处理请求。基本请求处理程序类BaseRequestHandler将所有操作都放在一个方法中——服务器自动调用的方法handle。这个方法可通过书信self.request来访问客户端套接字。如果处理的是流(TCPServer时很可能如此),可使用StreamRequestHandler类,它包含另外两个属性:self.rfile(用于读取)和self.wfile(用于写入)。你可以使用这两个类似与文件的对象来与客户端通信。
模块SocketServer还包含很多其他的类,它们为HTTP服务器提供基本的支持,以及XML-RPC支持。
from socketserver import TCPServer,StreamRequestHandler class Handle(StreamRequestHandler):
def handle(self):
addr = self.request.getpeername()
print("addr:", addr)
self.wfile.write("谢谢") server = TCPServer(('',1234), Handle)
server.serve_forever()
3 多个连接
处理多个连接的主要方式有三种:分叉(forking)、线程化和异步I/O。通过结合使用SocketServer中的混合类和服务器类,很容易实现分叉和线程化。但是,分叉占用资源较多,且在客户端很多时可伸缩性不高;而线程化可能带来同步问题。并且Windows不支持分叉,分叉是UNIX术语。
3.1 使用SocketServer实现分叉和线程化
使用框架SocketServer创建分叉或线程化服务器非常简单,一般仅当方法handle需要很长时间才能执行完毕是,分叉和线程化才能提供有效帮助。创建如下:
1.分叉服务器
from socketserver import TCPServer,StreamRequestHandler,ForkingMixIn class Server(ForkingMixIn, TCPServer):pass class Handle(StreamRequestHandler):
def handle(self):
addr = self.request.getpeername()
print("addr:", addr)
self.wfile.write("谢谢") server = Server(('',1234), Handle)
server.serve_forever()
2.线程化服务器
from socketserver import TCPServer,StreamRequestHandler,ThreadingMixIn class Server(ThreadingMixIn, TCPServer):pass class Handle(StreamRequestHandler):
def handle(self):
addr = self.request.getpeername()
print("addr:", addr)
self.wfile.write("谢谢") server = Server(('',1234), Handle)
server.serve_forever()
3.2 使用select和poll实现异步I/O
当服务器与客户端通信时,来自客户端的数据可能时断时续。如果使用了分叉和线程化,这就不是问题:因为一个进程(线程)等待数据时,其他进程(线程)可继续处理其客户端。然而,另一种做法是只处理正在通信的客户端。你甚至无需不断监听,只需监听后将客户端加入队列即可。
这就是框架asyncore/asynchat 和 Twisted采取的方法。这种功能的基石是函数select或poll(只支持UNIX系统)。这两个函数都位于模块select中,其中poll的可伸缩性更高。
函数select接收三个必不可少的参数和一个可选参数,其中前三个参数为序列,而第四个参数为超时时间(单位为秒)。这些序列包含文件描述符整数(也可以是包含返回文件描述符整数的方法fileno),表示我们正在等待的连接。这三个序列分别表示需要输入和输出以及发生异常(错误等)的连接。如果没有指定超时时间,select将阻断(即等待)到有文件描述符准备就绪;如果指定了超时时间,select将最多阻断指定的秒数;如果超时时间为0,select将不断轮询(即不阻断)。select返回三个序列(即一个长度为3的元祖),其中每个序列都包含相应参数中处于活动状态的文件描述符。
如下所示,一个使用select的简单服务器:import socket, select
s = socket.socket()
host = socket.gethostname()
port = 8080
s.bind((host,port))
s.listen(5)
inputs = [s]
while True:
# 方法select返回三个参数,通过序列解包赋值
rs, ws, es = select.select(inputs, [], [])
for r in rs:
if r is s:
c, addr = s.accept()
print("addr:", addr)
inputs.append(c)
else:
try:
data = r.reve(1024)
discon = not data
except socket.error:
discon = True
if discon:
# 1.getpeername():用于获取与某个套接字关联的外地协议地址
# 2.getsockname():用于获取与某个套接字关联的本地协议地址
print(r.getpeername(), 'discon')
inputs.remove(r)
else:
print(data)
方法poll 使用起来比select容易。调用poll 时,将返回一个轮询对象。你可以使用方法register 向这个对象注册文件描述符(或包含方法fileno的对象)。注册后可使用方法unregister 将它们删除。注册对象(如套接字)后,可调用其方法poll(它接受一个可选的超时时间参数)。这将返回一个包含(fd, event)元祖的列表(可能为空),其中fd为文件描述符,event是发生的事件。event是一个位掩码,这意味着它是一个整数,其各个位对应于不同的事件。各种事件是用select 模块中的常量表示的,如下表。要检查指定的位是否为1 (即是否发生了相应的事件),可使用按位与运算符( & ):
if event & select.POLLIN: pase
事件名 描述
POLLIN 文件描述符中有需要读取的数据
POLLPRI 文件描述符中有需要读取的 紧急数据
POLLOUT 文件描述符为写入数据做好了准备
POLLERR 文件描述符出现了错误状态
POLLHUP 挂起。连接已断开
POLLNVAL 无效请求。连接未打开
下面是一个使用poll的简单服务器:
import socket, select
s = socket.socket() host = socket.gethostname()
port = 8080
s.bind((host, port)) fdmp = {s.fileno(): s} s.listen(5)
p = select.poll()
p.register(s)
while True:
events = p.poll()
for fd, event in events:
if fd in fdmp:
c, addr = s.accept()
print("addr:", addr)
p.register(c)
fdmp[c.fileno()] = c
elif event & select.POLLIN:
data = fdmp[fd].recv(1024)
if not data:
print(fdmp[fd].getpeername(),"discon")
p.unregister(fd)
del fdmp[fd]
else:
print(data)
4 Twisted
这是Twisted Matrix Laboratories 开发的一个框架,功能丰富而复杂,支持大多数主要的网络协议。框架Twisted是异步的,因此效率和可伸缩性都非常高。对很多自定义网络应用程序来说,使用Twisted来开发可能是最佳选择。
python学习笔记(十 三)、网络编程的更多相关文章
- python学习笔记(十二)-网络编程
本文结束使用 Requests 发送网络请求.requests是一个很实用的Python HTTP客户端库,编写爬虫和测试服务器响应数据时经常会用到.可以说,Requests 完全满足如今网络的需求. ...
- 吴裕雄--天生自然python学习笔记:Python3 网络编程
Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的全部方法. 高级别的网络 ...
- python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例
python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...
- Python学习笔记(十二)—Python3中pip包管理工具的安装【转】
本文转载自:https://blog.csdn.net/sinat_14849739/article/details/79101529 版权声明:本文为博主原创文章,未经博主允许不得转载. https ...
- 4月18日 python学习总结 异常处理、网络编程
一. 异常 1.什么是异常 异常是错误发生的信号,程序一旦出错,如果程序中还没有相应的处理机制 那么该错误就会产生一个异常抛出来,程序的运行也随之终止 2.一个异常分为三部分: 1.异常的追踪信息 2 ...
- Python学习(十三) —— 网络编程
一.操作系统基础 操作系统(Operating System):OS是管理和控制计算机硬件和软件资源的计算机程序,是直接运行在“裸机”上的最基本的系统软件,任何其他软件都必须在操作系统的支持下才能运行 ...
- Android(java)学习笔记77:网络编程的概述
计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统. 网络编程 就 ...
- Android(java)学习笔记17:网络编程的概述
1. 计算机网络 计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统 ...
- Python学习笔记(三)字符串类型及其操作(2)
1.字符串的表示 字符串是字符的序列表示,可以由一对单引号(‘).双引号(“)或三引号(’‘’)构成.其中,单引号.双引号和三引号都可以表示单行字符串,但是只有三引号可以表示多行字符串 在使用双引号时 ...
随机推荐
- java将数据库中查询到的数据导入到Excel表格
1.Maven需要的依赖 <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --> <dependency> ...
- 用递归方法求n的阶乘
代码: #include<iostream> using namespace std; int fact(int n); int main() { int n; loop: cin > ...
- python-邮件提醒功能
当scrapy爬取完成以后会发送详细信息到邮箱 1.首先编写邮件发送模块 #!usr/bin/env python # -*- coding:utf-8 -*- """ ...
- vue学习笔记:在vue项目里面使用引入公共方法
首先新建一个文件夹:commonFunction ,然后在里面建立 一个文件common.js 建立好之后,在main.js里面引入这个公共方法 最后是调用这个公共方法 测试一下,我在公共方法里面写了 ...
- idea导入maven项目,找不到jar包,出现红色波浪线【转】
参考链接 点击跳转
- java内存分页计算
介绍三个最常用的分页算法 First(感觉这个最简单实用) //总记录数int rows=21; //每页显示的记录数int pageSize=5; //页数int pageSum=(rows-1)/ ...
- 快速理解Token,Cookie,Session
在Web应用中,HTTP请求是无状态的.即:用户第一次发起请求,与服务器建立连接并登录成功后,为了避免每次打开一个页面都需要登录一下,就出现了cookie,Session. Cookie Cookie ...
- 北大开源全新中文分词工具包:准确率远超THULAC、结巴分词
最近,北大开源了一个中文分词工具包,它在多个分词数据集上都有非常高的分词准确率.其中广泛使用的结巴分词误差率高达 18.55% 和 20.42,而北大的 pkuseg 只有 3.25% 与 4.32% ...
- numpy.random 常用函数详解之简单随机数篇(Simple random data)
1.numpy.random.rand(d0,d1,d2,...,dn) 参数:d0,d1,d2,...,dn 须是正整数,用来描述生成随机数组的维度.如(3,2)代表生成3行2列的随机数组. 返回值 ...
- [Swift]LeetCode628. 三个数的最大乘积 | Maximum Product of Three Numbers
Given an integer array, find three numbers whose product is maximum and output the maximum product. ...