1. socket 发送请求
b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n
这就是固定格式,b字节,get 后必须跟空格然后/ s?wd=alex是百度固定的后缀 后面再来空格 HTTP/1.0\r\n 这也是固定格式 后面再带个host:www.baidu.com同固定格式\r\n\r\n两个结尾
2.解除阻塞
import socket client = socket.socket() client.setblocking(False) # 将原来阻塞的位置变成非阻塞(报错) # 百度创建连接: 阻塞 try: client.connect(('www.baidu.com',80)) # 执行了但报错了 except BlockingIOError as e: pass # 检测到已经连接成功 # 问百度我要什么? client.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n') # 我等着接收百度给我的回复 chunk_list = [] while True: chunk = client.recv(8096) # 将原来阻塞的位置变成非阻塞(报错) if not chunk: break chunk_list.append(chunk) body = b''.join(chunk_list) print(body.decode('utf-8'))
3 io多路复用,检测多个socket是否已经发生变化(是否已经连接成功,是否可以获得数据)可读/可写
import socket import select client1 = socket.socket() client1.setblocking(False) # 百度创建连接: 非阻塞 try: client1.connect(('www.baidu.com',80)) except BlockingIOError as e: pass client2 = socket.socket() client2.setblocking(False) # 百度创建连接: 非阻塞 try: client2.connect(('www.sogou.com',80)) except BlockingIOError as e: pass client3 = socket.socket() client3.setblocking(False) # 百度创建连接: 非阻塞 try: client3.connect(('www.oldboyedu.com',80)) except BlockingIOError as e: pass socket_list = [client1,client2,client3] conn_list = [client1,client2,client3] while True: rlist,wlist,elist = select.select(socket_list,conn_list,[],0.005) #select检测里面的数据有没有变化,如果有人给他发数据,就会检测到变化 他会接收3个元组 但凡rlist有值就是数据返回回来了,但凡wlist返回回来就是连接成功 # wlist中表示已经连接成功的socket对象 for sk in wlist: if sk == client1: sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n') elif sk==client2: sk.sendall(b'GET /web?query=fdf HTTP/1.0\r\nhost:www.sogou.com\r\n\r\n') else: sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.oldboyedu.com\r\n\r\n') conn_list.remove(sk) for sk in rlist: chunk_list = [] #故技重施 while True: try: chunk = sk.recv(8096) if not chunk: break chunk_list.append(chunk) except BlockingIOError as e: break body = b''.join(chunk_list) # print(body.decode('utf-8')) print('------------>',body) sk.close() socket_list.remove(sk) if not socket_list: break
4.twisted,基于事件循环实现的异步非阻塞框架 非阻塞:不等待
异步:执行完某个任务后自用调用我给他的函数。 自动下载完回调自己的函数,谁都不等待谁
1.socket 默认是阻塞的 阻塞在连接时,和接收时
2. setblocking(False) 把他变成非阻塞的
3。io多路复用作用为,检测多个socket是否发生了变化 实际是操作系统检测socket是否发生变化,有三种模式
select:限制为1024个socket;内部检测是用的循环
poll:不限个数 但还是内部循环(水平触发)
epoll:不限个数,会调方式(边缘触发)
python中的模块:
select.select
select.epoll win没有
4。提高并发的方案
多进程
多线程
单线程异步非阻塞模块(twisted)scrapy框架(单线程完成并发)
5.异步非阻塞
非阻塞,不等待,比如创建socket对某个地址进行connect,获取接收数据recv时默认都会等待连接成功,或接收到数据,才执行后续操作,如果设置成setblocking(False)以上两个过程都不再等待但是回报blockingIOError的错,只要捕获即可,
异步,执行完成之后自动执行回调函数或自动执行某些操作(通知)。
比如做爬虫中向某个地址xxx发送请求,当请求执行完成之后自动执行回调函数。
6.同步阻塞
阻塞:等
同步:按照顺序逐步执行
io多路复用,和
7.在别人不改变的时候,你还想请求完成,只能自己改变
协程
进程,操作系统中存在;
线程,操作系统中存在;
协程,是由程序员创造出来的一个不是真实存在的东西;
协程,是微线程。对一个线程进行分片,使得线程在代码块之间进行来回切换执行,而不是在原来逐行执行,单纯协程没用,甚至性能降低
import greenlet def f1(): print(11) gr2.switch() print(22) gr2.switch() def f2(): print(33) gr1.switch() print(44) # 协程 gr1 gr1 = greenlet.greenlet(f1) # 协程 gr2 gr2 = greenlet.greenlet(f2) gr1.switch() 微协程写完了
协程+遇到IO就切换=>牛逼起来了
from gevent import monkey monkey.patch_all() # 以后代码中遇到IO都会自动执行greenlet的switch进行切换 import requests import gevent #上面这些一定要写 def get_page1(url): ret = requests.get(url) print(url,ret.content) gevent.joinall([ gevent.spawn(get_page1, 'https://www.python.org/'), # 协程1 gevent.spawn(get_page1, 'https://www.yahoo.com/'), # 协程2 gevent.spawn(get_page1, 'https://github.com/'), # 协程3 ])
协程也可以称之为 微线程,就是开发者控制线程执行流程,控制先执行某段代码,然后再切换到另外函数执行代码。。。来回切换。
协程不一定可以提高并发,协程自己本身无法提高啊并发,(甚至会降低)。协程+IO切换性能提高。
进程线程区别。
进程是计算机资源分配的最小单元,主要做数据隔离,线程是工作的最小单元,主要工作的就是线程,一个进程里可以有多个线程,一个应用程序里可以有多个进程,应用场景在其他语言里基本没有进程概念,都是用的线程,在python里io操作多的时候,用线程,计算密集型的时候,用进程,因为py有GIL锁,锁住了一个进程中同一时刻只能调用一个线程,无法把cpu完全使用,多线程io不占cpu,程序员为了优化代码,创造了协程,本身不存在,协程本身让人为控制,先执行这个后执行那个,本来没用配合io就有用了,遇到io就执行别的,遇到i o切换到别的地方执行了,把线程分片了,就是让线程一直不停,一直工作,遇到io就切换到别的地方工作,io操作回来的之后再切回来,这点区别吧,py用协程的时候,要用到 greenlet 模块,实现io加协程自动切换的模块gevent
4.单线程提供并发:
协程+io切换 gevent
基于事件循环的异步非阻塞框架:twisted