1. socket 发送请求
b'GET /s?wd=alex HTTP/1.0 host:www.baidu.com
这就是固定格式,b字节,get 后必须跟空格然后/ s?wd=alex是百度固定的后缀 后面再来空格 HTTP/1.0 这也是固定格式 后面再带个host:www.baidu.com同固定格式 两个结尾
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
host:www.baidu.com
')
# 我等着接收百度给我的回复
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
host:www.baidu.com
')
elif sk==client2:
sk.sendall(b'GET /web?query=fdf HTTP/1.0
host:www.sogou.com
')
else:
sk.sendall(b'GET /s?wd=alex HTTP/1.0
host:www.oldboyedu.com
')
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