25.网络编程(上)
decude("utf-8") #解码
encode("utf-8") #编码
概念
网络架构
C/S :qq 微信 浏览器 英雄联盟 穿越火线 王者荣耀 安装
C:client 客户端
S:server 服务端
B/S :百度 淘宝 码云 只要在浏览器输入网址就可以直接使用了
B:browser 浏览器
S:server 服务端
B/S 架构中的浏览器 也是客户端的一种
B/S 是C/S架构中的一种
B/S更好: 更节省资源 不用更新 不依赖环境
统一了所有web程序的入口
C/S架构: 安全性 程序比较庞大
移动端
app
微信小程序 : 统一了所有web程序的入口
支付宝 : 统一了所有和钱相关的事儿
mac地址
是一个物理地址
唯一的标识你的网络设备
ip地址
是一个逻辑地址
是可以根据你的位置变化发生改变的
能够在广域网中快速的定位你
ipv4地址
4位点分十进制
0.0.0.0-255.255.255.255
2**32
公网和内网
公网 0.0.0.0-255.255.255.255(不包含保留字段的ip)
你能够在任意一个地方去访问的ip地址
内网 所有的内网ip都要使用保留字段
只能在一个区域内使用,出了这个区域就用不了了
192.168.0.0 - 192.168.255.255
10.0.0.0 - 10.255.255.255
172.16.0.0 - 172.32.255.255
路由器和交换机
交换机完成局域网内通信
通过ip找mac地址 : arp协议
单播 广播 组播
路由器完成局域网间通信
网关ip
子网掩码(了解)
255.0.0.0
255.255.0.0
255.255.255.0
ip 和子网掩码 按位与运算
192.168.13.26 # 255.255.0.0
11000000.10101000.00001101.00011010
11111111.11111111.00000000.00000000
11000000.10101000.00000000.00000000 = 192.168.0.0
192.168.12.134
255.255.0.0
11000000.10101000.00001100.10000110
11111111.11111111.00000000.00000000
11000000.10101000.00000000.00000000 = 192.168.0.0
ipv6
6位冒分十六进制
0:0:0:0:0:0 - FFFF:FFFF:FFFF:FFFF:FFFF:FFFF
mac ip 定位到一台机器
port 端口
0-65535
ip + port能够唯一的确认网络上的一台机器上的一个服务
网络编程(下)
socket(套接字)
osi协议
tcp协议
内部执行流程
发送端将数据通过操作系统写入缓存内,双方在通过网卡进行传输,发送到对方电脑内存在通过操作系统将数据交给接受端进行操作
tcp协议三次握手
tcp协议四次挥手
tcp协议黏包
首先我们先来了解一下tcp的传输模式
上面就是流式传输中间不会有任何阻隔,如果连着传输文件就会形成黏包
所以为了防止出现黏包我们有以下几个方法
1.time
用time.sleep在连续传输多个数据中间进行暂停来防止黏包,可这样会大大降低程序的效率
(因为现在的cpu每秒可以运行特别特别特别多的程序行)
2.利用对传输数据求长度,从而限制接受长度
首先先传输一个固定长度的数据长度数据
假设想长度是100是3位咱们规定接受的长度为10位所以就将数据转换为0000000100
对方接收到后用int()将前面的0给去掉获得数据长度
下一回接收的时候就可以使用这个长度进行接收
服务端
import socket
sk = socket.socket()
sk.bind(('192.168.12.26',9001))
sk.listen()
conn,addr = sk.accept()
msg1 = b'hello'
msg2 = b'world'
len_msg = len(msg1)
str_len = str(len_msg)
proto_len = str_len.zfill(10)
conn.send(proto_len.encode())
conn.send(msg1)
len_msg = len(msg2)
str_len = str(len_msg)
proto_len = str_len.zfill(10)
conn.send(proto_len.encode())
conn.send(msg2)
客户端
import time
import socket
sk = socket.socket()
sk.connect(('192.168.12.26',9001))
for i in range(1000000):2*i
len_msg = sk.recv(10).decode('utf-8')
len_msg = int(len_msg)
msg1 = sk.recv(len_msg)
print(msg1)
len_msg = sk.recv(10).decode('utf-8')
len_msg = int(len_msg)
msg2 = sk.recv(len_msg)
print(msg2)
3.使用struct内置模块
1.osi七层协议
7.应用层
6.表示层
5.会话层
4.传输层
3.网络层
2.数据链路层
1.物理层
2.osi五层协议
5.应用层
4.传输层 :端口,tcp协议,udp协议 ,硬件:四层交换机,四层路由器
3.网络层 :ipv4 ipv5协议 ,硬件:路由器,三层交换机
2.数据链路层 :mac arp协议 ,硬件:交换机(二层交换机),网卡
1.物理层
3.tcp协议 (应用在 :上传下载发邮件 特点 :可靠 面向连接 速度慢 传递的数据程度无限)
建立连接 三次握手
信息传递 可靠传输
断开连接 四次挥手
4.udp协议(应用在 :即时通信工具 特点 :不可靠 面向数据报(报文) 速度快 传递的数据长度有限)
不管对方在不在 直接发送信息
不占连接
随时可以收发信息
1.简单的聊天小程序
tcp协议版本
s服务端py文件
import socket #调用socket
tx = socket.socket() #实例化一个socket对象
tx.bind(("127.0.0.1", 9997)) #将实例化的对象绑定一个ip和端口
while 1:
tx.listen() #对这个对象进行监听
conn, addr = tx.accept() #接受一个聊天请求和一个地址
z = input("请输入您的聊天内容") #接受您的聊天内容
if z == "q":
break
conn.send(z.encode("utf-8")) #设置传递文字
msg = conn.recv(1024).decode("utf-8") #设置传递时的长度和编码格式
print(msg) #输出返回值
conn.close() #关闭本次请求
tx.close() #关闭整个socket对象
c客户端py文件
import socket
while 1:
tx = socket.socket()
tx.connect(("127.0.0.1", 9999))
lt = tx.recv(1024)
print(lt)
z = input("请输入聊天内容")
if z == "q":
break
tx.send(z.encode("utf-8"))
tx.close()
2.udp实现简单的网络聊天小程序
udp协议
s服务端py文件
from socket import socket,SOCK_DGRAM
lj = socket(type = SOCK_DGRAM)
lj.bind(("127.0.0.1",9999))
a,b = lj.recvfrom(1024)
print(a)
lj.sendto(b"kksjdfkjlasjfklas",b)
c客户端py文件
from socket import socket,SOCK_DGRAM
dx = socket(type = SOCK_DGRAM)
d = ("127.0.0.1",9999)
dx.sendto(b"hkjjkhk",d)
a = dx.recv(1024)
print(a)
3.解决tcp黏包实现文件上传下载
(黏包的解决)
tcp实现文件上传下载功能
在经过交换机等硬件设备时会遵循网络传输的最大长度mtu(1500)
介绍一个内置模块
struct
struct.pack(整形) #将你想要传输的数据长度放到括号内,返回一个4字节
struct.unpack(将被pack后的字节转换为元数据) #将被pack后的字节转换为元数据,返回一个元祖
实现进度条功能
import sys
def processBar(num, total):
rate = num / total
rate_num = int(rate * 100)
if rate_num == 100:
r = '\r%s>%d%%\n' % ('=' * rate_num, rate_num,)
else:
r = '\r%s>%d%%' % ('=' * rate_num, rate_num,)
print(r,flush=True)
processBar(2048,10240)
服务端
import socket
import struct
lj = socket.socket()
lj.bind(("127.0.0.1",9999))
lj.listen()
n,m = lj.accept()
def j():
global n
myn = n.recv(4)
z = struct.unpack("i",myn)
myn = n.recv(z[0])
f = open("cwj","ab")
f.write(myn)
f.close()
return
def b():
global n
f = open("swj","rb")
wj = f.read()
f.close()
z = struct.pack("i",len(wj))
n.send(z)
n.send(wj)
return
v = n.recv(2)
if v == b"1":
j()
elif v == b"2":
b()
n.close()
lj.close()
客户端
import socket
import struct
lj = socket.socket()
lj.connect(("127.0.0.1",9999))
def j():
a = lj.recv(4)
z = struct.unpack("i",a)
a = lj.recv(z[0])
f = open("swj","ab")
f.write(a)
f.close()
return
def b():
f = open("cwj","rb")
wj = f.read()
f.close()
z = struct.pack("i", len(wj))
lj.send(z)
lj.send(wj)
return
x = input("""
1.上传
2.下载
请选择您的操作
""")
if x == "1":
lj.send("1".encode("utf-8"))
b()
elif x == "2":
lj.send("2".encode("utf-8"))
j()
else:
print("请输入合法序号")
lj.close()
4操作系统基础
1.一开始人们在读取数据的时候使用的是穿孔卡片
2.过段时间后大家发现穿孔卡片这个方法太过缓慢,于是使用了新的技术高速磁带
3.使用了高速磁带后速度有了质的提升,但是还是有着很多需要优化的地方
多道操作系统
由于cpu在遇到IO操作的时候会暂停计算进行休息
所以就会大量降低效率,因此我们就引进了一个
新的算法 多道操作系统多道操作系统在cpu执行
程序的时候如果遇到IO操作就会结束着个程序直接
运行下一个任务
总结
# 穿孔卡片
# 高速磁带
# -- 操作系统
# 多道操作系统
# 第一次提出了多个程序可以同时在计算机中被计算
# 1.遇到IO就让出CPU
# 2.把CPU让给其他程序,让其他程序能够使用CPU
# 3.CPU的让出这件事 占用时间
# 4.两个程序来回在CPU上切换,不会
# 每个程序有独立的内存空间
# 每个程序在切换的前后会把当前程序的状态记录下来
# CPU计算和不计算(IO)操作
# IO操作(网络操作\文件操作) : 输入输出:相对内存
# 阻塞: sleep\input\recv\accept\recvfrom是不需要cpu参与的
# 对文件的读取 : 对硬盘的操作一次读取相当于90w条代码
# Input : 向内存输入数据
# 读\load\input\recv\recvfrom\accept\connect\close
# Output : 从内存输出数据
# 写\dump\print\send\sendto\accept\connect\close
# 所有的IO操作本质都是文件操作
# input\print input是写入文件,然后通过读取文件把输入的内容加载到内存
# print是直接写入文件,然后通过文件展示给用户看
# socket中的交互方法 : 都是文件操作
# send 是向缓存文件中写
# recv 是从缓存文件中读
# 也就是说只要涉及到IO操作 至少就是一个0.009s=就是CPU执行90w条python代码的时间
# 0.009s
# 500000000条指令/s /5 = 100000000条python代码/s
# 0.009s * 100000000 = 900000条python代码
# import dis
# a = 1
# def func():
# global a
# a+=1
# dis.dis(func)
# 1.老教授和研究生
# 研究生 5min 没有IO操作 先来先服务(FIFS)
# 老教授 24h 没有IO操作
# 研究生 3min 没有IO操作 短作业优先算法
# 2.时间片轮转算法 -- 分时操作系统
# 1w = 0.00005s
# 1.时间片到了才让出CPU
# 2.CPU的让出这件事 占用时间
# 3.减低工作效率,提高了用户体验
5.进程初始
总结
# 程序与进程(计算机中最小的资源分配单位)
# 运行中的程序 就是 进程
# 进程与进程之间的数据是隔离的
# 线程(计算机中能被操作系统调度的最小单位)
# 每个程序执行到哪个位置是被记录下来的
# 在进程中 有一条线程是负责具体的执行程序的
# 进程的调度(由操作系统完成的) :
# 被操作系统调度的,每个进程中至少有一个线程
# 短作业优先算法
# 先来先服务算法
# 时间片轮转算法
# 多级反馈算法
# 进程的启动 销毁
# 进程的启动 : 交互(双击) 在一个进程中启动另一个 开机自启动
# 负责启动一个进程的程序 被称为一个父进程
# 被启动的进程 被成为一个子进程
# 销毁 : 交互 被其他进程杀死(在父进程结束子进程) 出错进程结束
# 父子进程
# 父进程开启子进程
# 父进程还要负责对结束的子进程进行资源的回收
# 进程id --> processid --> pid
# 在同一台机器上 同一个时刻 不可能有两个重复的进程id
# 进程id不能设置 是操作系统随机分配的
# 进程id随着多次运行一个程序可能会被多次分配 每一次都不一样
# 进程的三状态图
# 就绪ready 运行run 阻塞block
# import os
# import time
#
# print(os.getpid())
# print(os.getppid()) # parent process id
# time.sleep(100)
# 2.模块multiprocessing模块 :内置模块
# multiple 多元化的
# processing 进程
# 把所有和进程相关的机制都封装在multiprocessing模块中了
# 3.学习这个模块
import os
import time
from multiprocessing import Process
def func():
'''
在子进程中执行的func
:return:
'''
print('子进程 :',os.getpid(),os.getppid())
time.sleep(3)
if __name__ == '__main__':
p = Process(target=func)
p.start()
print('主进程 :',os.getpid())
# 并行 : 多个程序同时被CPU执行
# 并发 : 多个程序看起来在同时运行
# 同步 : 一个程序执行中再调用另一个 并且在调用的过程中还要等待这个程序执行完毕
# 异步 : 一个程序在执行中调用了另一个 但是不等待这个任务完毕 就继续执行 start
# 阻塞 : CPU不工作
# 非阻塞 : CPU工作
1.进程模块multiprocessing
2.process(进程)
# start() 开启子进程
# join() 阻塞主进程等待子进程执行结束
# terminate() 暂停子进程
# is_alive() 判断进程是否存活
import os
import time
from multiprocessing import Process
#
# def son_process():
# print('strat son',os.getpid())
# time.sleep(50)
# print('end son')
#
# if __name__ == '__main__':
# print(os.getpid())
# Process(target=son_process).start()
# 主进程是在子进程执行完毕之后才结束
# 主进程回收子进程的资源
# 多个子进程
# def son_process():
# print('strat son',os.getpid())
# time.sleep(1)
# print('end son')
#
# if __name__ == '__main__':
# print(os.getpid())
# Process(target=son_process).start()
# Process(target=son_process).start()
# Process(target=son_process).start()
# def son_process():
# print('strat son',os.getpid())
# time.sleep(1)
# print('end son')
#
# if __name__ == '__main__':
# for i in range(5):
# Process(target=son_process).start()
join
(形成堵塞等待子进程执行完毕)
# import time
# def son_process():
# time.sleep(4)
# print('通知xxx用户,机器挂掉了')
#
# if __name__ == '__main__':
# p = Process(target=son_process)
# p.start()
# p.join() # 阻塞,直到p对应的子进程对象执行结束
# print('所有任务结束')
# import time
# def son_process(n):
# print('start', n)
# time.sleep(2)
# print('end',n)
#
# if __name__ == '__main__':
# p_l = []
# for i in range(10):
# p = Process(target=son_process,args=(i,))
# p.start() # start相当于告诉操作系统要开启一个子进程,而子进程的调度是由操作系统控制的
# p_l.append(p)
# for p in p_l:p.join() # join 如果执行这句话的时候子进程已经结束了,那么join就不阻塞了
# print('所有任务结束')
守护进程
(守护进程会保护主进程代码,在主进程代码执行完毕后会让操作系统对子进程进行关闭然后会自己把让操作系统关闭自己)
# 守护进程会随着父进程的代码结束而结束
# import time
# def son():
# while True:
# time.sleep(1)
# print('in son')
#
#
# if __name__ == '__main__':
# p = Process(target=son)
# p.daemon = True # 将当前的子进程设置为守护进程
# p.start()
# time.sleep(5)
# 正常情况下 父进程永远会等着子进程结束
# 如果设置了守护进程 父进程的代码结束之后 守护进程也跟着结束
# 子进程结束之后,父进程才会结束
# 代码结束和进程结束是两回事儿
# 没设置守护进程
# 1.子进程的代码和主进程的代码自己执行自己的,互相之间没关系
# 2.如果主进程的代码先结束,主进程不结束,等子进程代码结束,回收子进程的资源,主进程才结束
# 3.如果子进程的代码先结束,主进程边回收子进程的资源边执行自己的代码,当代码和资源都回收结束,主进程 才结束
# 设置了守护进程
# 1.子进程的代码和主进程的代码自己执行自己的,互相之间没关系
# 2.一旦主进程的代码先结束,主进程会先结束掉子进程,然后回收资源,然后主进程才结束
# import time
# def son():
# while True:
# time.sleep(1)
# print('in son')
#
# def son2():
# print('start son2')
# time.sleep(10)
# print('end son2')
#
# if __name__ == '__main__':
# p = Process(target=son)
# p.daemon = True
# p.start()
# Process(target=son2).start()
# time.sleep(5)
# 守护进程不会守护除了主进程代码之外的其他子进程
# 偏要求守护进程在子进程结束之后才结束
# import time
# def son():
# while True:
# time.sleep(1)
# print('in son')
#
# def son2():
# print('start son2')
# time.sleep(10)
# print('end son2')
#
# if __name__ == '__main__':
# p = Process(target=son)
# p.daemon = True
# p.start()
# p = Process(target=son2)
# p.start()
# time.sleep(5)
# p.join()
# Process对象的方法
# start() 开启子进程
# join() 阻塞主进程等待子进程执行结束
# terminate() 暂停子进程
# is_alive() 判断进程是否存活
# import time
# def son():
# while True:
# time.sleep(1)
# print('in son')
#
# if __name__ == '__main__':
# p = Process(target=son)
# p.start()
# time.sleep(5)
# print(p.is_alive())
# p.terminate() # 异步非阻塞操作
# time.sleep(0.1)
# print(p.is_alive())
# print('我还可以继续做其他的事情,主进程的代码并不结束')
# 面向对象的方式开启子进程
# class MyProcess(Process):
# def run(self):
# print(os.getpid())
#
# if __name__ == '__main__':
# print('主 :',os.getpid())
# MyProcess().start()
# 传参数
# class MyProcess(Process):
# def __init__(self,arg1,arg2):
# super().__init__() # 执行父类的init
# self.a1 = arg1
# self.a2 = arg2
# def run(self):
# print(os.getpid(),self.a1,self.a2)
#
# if __name__ == '__main__':
# print('主 :',os.getpid())
# MyProcess(1,2).start()
# 进程之间的数据是否隔离
# n = 0
# def son():
# global n
# n += 1
# for i in range(20):
# son()
# print(n)
n = 0
def son():
global n
n += 1
if __name__ == '__main__':
p_l = []
for i in range(20):
p = Process(target=son)
p.start()
p_l.append(p)
for p in p_l:p.join()
print(n)
2.锁(进程)
lock.acquire() # 给这段代码上锁
lock.release() # 给这段代码解锁
# 数据安全(锁) :用来保证数据安全
# 如果多个进程同时对一个文件进行操作会出现什么问题
# 1.读数据 : 可以同时读
# 2.写数据 : 但不能同时写
from multiprocessing import Process,Lock
def change(lock):
print('一部分并发的代码,多个进程之间互相不干扰的执行着')
lock.acquire() # 给这段代码上锁
with open('file','r') as f:
content = f.read()
num = int(content)
num += 1
for i in range(1000000):i+=1
with open('file','w') as f:
f.write(str(num))
lock.release() # 给这段代码解锁
print('另一部分并发的代码,多个进程之间互相不干扰的执行着')
if __name__ == '__main__':
lock = Lock()
for i in range(10):
Process(target=change,args=(lock,)).start()
# 当多个进程同时操作文件/共享的一些数据的时候就会出现数据不安全
# 开启多进程 同时执行100000行代码
# 其中20行涉及到了操作同一个文件
# 只给这20行代码枷锁,来保证数据的安全
3.进程通信
# 进程之间通信 IPC
# Inter Process Communication
from multiprocessing import Queue,Process
# def son(q):
# print(q.get())
#
# if __name__ == '__main__':
# q = Queue()
# p = Process(target=son,args=(q,))
# p.start()
# q.put(123)
# 在进程之间维护数据的安全 -- 进程安全
# 队列是进程安全的(进程队列保证了进程的数据安全)
# 队列都是先进先出的
# 队列是基于文件 + 锁实现的
# 队列一共提供两个方法:get put
# q = Queue()
# q.put({1,2,3})
# num = q.get() # get是一个同步阻塞方法,会阻塞直到数据来
# print(num)
# q = Queue(2)
# q.put({1,2,3})
# q.put({1,2,3})
# q.put({1,2,3}) # put是一个同步阻塞方法,会阻塞直到队列不满
import queue
# q = Queue(2)
# try:
# for i in range(4):
# q.put_nowait(i) # put_nowait 同步非阻塞方法
# except queue.Full:
# print(i)
# q2 = Queue(2)
# try:
# print(q2.get_nowait())
# except queue.Empty:pass
# q = Queue(5)
# ret = q.qsize() # 查看当前队列有多少值
# print(q.empty())
# print(q.full())
# 生产者消费者模型
# 队列Queue = 管道Pipe + 锁
# Pipe 基于文件实现的(socket+pickle) = 数据不安全
# Queue 基于文件(socket+pickle)+锁(lock)实现的 = 数据安全
# 基于pipe+锁(lock)实现的
# IPC:
# 内置的模块实现的机制 :队列\管道
# 第三方工具 : redis rabbitMQ memcache
7.线程
首先咱们先来复习一下之前的一些内容
面型对象继承复习
class Thread(object):
def __init__(self):
pass
def start(self):
self.run()
def run(self):
print('thread.run')
obj = Thread()
obj.start()
-----------------------------------
class MyThread(Thread):
def run():
print('mythread.run')
obj = MyThread()
obj.start()
面向对象上下文管理
class Foo:
def __enter__(...):
pass
def __exit__(...):
pass
lock = Foo()
with lock:
pass
不完整单例模式
class Foo:
instance = None
def __init__(self, name):
self.name = name
def __new__(cls, *args, **kwargs):
# 返回空对象
if cls.instance:
return cls.instance
cls.instance = object.__new__(cls)
return cls.instance
obj1 = Foo('日魔')
obj2 = Foo('SB')
print(obj1,obj2)
1小爬虫
咱们先介绍一个简单的爬虫小代码(了解即可因为后面需要)
1. 安装第三方模块
pip3 install requests
2. 使用
import requests
url = 'https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg'
# python伪造浏览器向地址发送请求
rep = requests.get(url)
# 请求返回回来的字节
# print(rep.content)
with open('xxxxxx.jpg',mode='wb') as f:
f.write(rep.content)
补充
import requests
video_url = 'https://aweme.snssdk.com/aweme/v1/playwm/?video_id=v0300f0c0000blvmurlqukgb6o87iq1g&line=0'
"""
headers中的设置是固定搭配,将python代码伪造成是谷歌浏览器的访问。
"""
response = requests.get(
url=rul,
headers={
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}
)
with open('xxxx.mp4', 'wb') as f:
for chunk in response.iter_content():
f.write(chunk)
爬取汽车之家图片小程序
import requests
url_list = [
'https://www3.autoimg.cn/newsdfs/g26/M02/35/A9/120x90_0_autohomecar__ChsEe12AXQ6AOOH_AAFocMs8nzU621.jpg',
'https://www2.autoimg.cn/newsdfs/g30/M01/3C/E2/120x90_0_autohomecar__ChcCSV2BBICAUntfAADjJFd6800429.jpg',
'https://www3.autoimg.cn/newsdfs/g26/M0B/3C/65/120x90_0_autohomecar__ChcCP12BFCmAIO83AAGq7vK0sGY193.jpg'
]
for url in url_list:
ret = requests.get(url)
file_name = url.rsplit('/',maxsplit=1)[-1]
with open(file_name,mode='wb') as f:
# 下载小文件
# f.write(ret.content)
# 下载大文件
for chunk in ret.iter_content():
f.write(chunk)
2线程的概念&与进程的区别?
形象的关系
- 工厂 -> 应用程序
- 车间 -> 进程
- 工人 -> 线程
进程和线程的区别?
进程是计算机资源分配的最小单位.
线程是计算机中可以被cpu调度的最小单位.
一个进程中可以有多个线程,同一个进程中的线程可以共享此进程中的资源,一个进程中至少有一个线程(一个应用程序中至少有一个进程)
在Python中因为有GIL锁,他同.....
默认进程之间无法进行资源共享,如果主要想要通讯可以基于:文件/网络/Queue.
3多线程快速应用
import threading
def task(arg):
pass
# 实例化一个线程对象
t = threading.Thread(target=task,args=('xxx',))
# 将线程提交给cpu
t.start()
import threading
def task(arg):
ret = requests.get(arg)
file_name = arg.rsplit('/', maxsplit=1)[-1]
with open(file_name, mode='wb') as f:
f.write(ret.content)
for url in url_list:
# 实例化一个线程对象
t = threading.Thread(target=task,args=(url,))
# 将线程提交给cpu
t.start()
t.start()
将线程提交给cpu,由cpu来进行调度(例子和join放在了一起)
t.join()
等待
import threading
# 示例1
"""
loop = 10000000
number = 0
def _add(count):
global number
for i in range(count):
number += 1
t = threading.Thread(target=_add,args=(loop,))
t.start()
t.join()
print(number)
"""
# 示例2
"""
loop = 10000000
number = 0
def _add(count):
global number
for i in range(count):
number += 1
def _sub(count):
global number
for i in range(count):
number -= 1
t1 = threading.Thread(target=_add,args=(loop,))
t2 = threading.Thread(target=_sub,args=(loop,))
t1.start()
t2.start()
print(number)
"""
# 示例3
"""
loop = 10000000
number = 0
def _add(count):
global number
for i in range(count):
number += 1
def _sub(count):
global number
for i in range(count):
number -= 1
t1 = threading.Thread(target=_add,args=(loop,))
t2 = threading.Thread(target=_sub,args=(loop,))
t1.start()
t2.start()
t1.join() # t1线程执行完毕,才继续往后走
t2.join() # t2线程执行完毕,才继续往后走
print(number)
"""
# 示例4
"""
loop = 10000000
number = 0
def _add(count):
global number
for i in range(count):
number += 1
def _sub(count):
global number
for i in range(count):
number -= 1
t1 = threading.Thread(target=_add,args=(loop,))
t2 = threading.Thread(target=_sub,args=(loop,))
t1.start()
t1.join() # t1线程执行完毕,才继续往后走
t2.start()
t2.join() # t2线程执行完毕,才继续往后走
print(number)
"""
t.setDaemon()
设置成为守护线程
import threading
import time
def task(arg):
time.sleep(5)
print('任务')
t = threading.Thread(target=task,args=(11,))
t.setDaemon(True)
t.start()
print('END')
t.setName()
线程名称的设置和调用
import threading
def task(arg):
# 获取当前执行此代码的线程
name = threading.current_thread().getName()
print(name)
for i in range(10):
t = threading.Thread(target=task,args=(11,))
t.setName('日魔-%s' %i )
t.start()
run()
自定义线程时,cpu调度执行的方法
class RiMo(threading.Thread):
def run(self):
print('执行此线程',self._args)
obj = RiMo(args=(100,))
obj.start()
线程安全(锁)
多个线程同时去操作一个"东西",不要存在数据混乱.
线程安全: logging模块 / 列表
线程不安全: 自己做文件操作 / 同时修改一个数字
使用锁来保证数据安全,来了多个线程,使用锁让他们排队,逐一执行
死锁有两种出现的场景1.利用互斥锁(Lock),对同一段段代码进行双层上锁,会出现死锁
2.利用递归锁(RLock),做交叉锁,会出现死锁
交叉使用实例
import threading
import time
lock1 = threading.RLock()
lock2 = threading.RLock()
def fenglin():
lock1.acquire()
time.sleep(1)
lock2.acquire()
def rimo():
lock2.acquire()
time.sleep(1)
lock1.acquire()
t1 = threding.Thread(target=fenglin)
t2 = threding.Thread(target=rimo)
t1.start()
t2.start()
Lock
互斥锁
import threading
import time
num = 0
# 线程锁
lock = threading.Lock()
def task():
global num
# # 申请锁
# lock.acquire()
# num += 1
# time.sleep(0.2)
# print(num)
# # 释放锁
# lock.release()
with lock:
num += 1
time.sleep(0.2)
print(num)
for i in range(10):
t = threading.Thread(target=task)
t.start()
RLock
递归锁
import threading
import time
num = 0
# 线程锁
lock = threading.RLock()
def task():
global num
# 申请锁
lock.acquire()
num += 1
lock.acquire()
time.sleep(0.2)
print(num)
# 释放锁
lock.release()
lock.release()
for i in range(10):
t = threading.Thread(target=task)
t.start()
GIL
GIL,全局解释器锁.
同一时刻保证一个进程中只有一个线程可以被cpu调度,所以在使用Python开发时要注意:
计算密集型,用多进程.
IO密集型,用多线程.
* Python中如果创建多现场无法应用计算机的多核优势
例子
利用threading模块实现并发socket
import socket
import threading
def task(connect,address):
pass
server = socket.socket()
server.bind(('127.0.0.1',9000))
server.listen(5)
while True:
conn,addr = server.accept()
# 处理用户请求
t = threading.Thread(target=task,args=(conn,addr,))
t.start()
利用锁实现一个完整的单例模
import threading
import time
class Singleton:
instance = None
lock = threading.RLock()
def __init__(self, name):
self.name = name
def __new__(cls, *args, **kwargs):
if cls.instance:
return cls.instance
with cls.lock:
if cls.instance:
return cls.instance
time.sleep(0.1)
cls.instance = object.__new__(cls)
return cls.instance
def task():
obj = Singleton('x')
print(obj)
for i in range(10):
t = threading.Thread(target=task)
t.start()
# 执行1000行代码
data = Singleton('asdfasdf')
print(data)
协程
简单的携程
需要下载第三方模块
简单的携程
from greenlet import greenlet
def test1():
print('i1')
gr2.switch()
print('i3')
gr2.switch()
def test2():
print('i2')
gr1.switch()
print('i4')
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
携程加IO切换
需要下载第三方模块
from gevent import monkey
monkey.patch_all()
import gevent
import time
def eat():
print('eat food 1')
time.sleep(3)
print('eat food 2')
def play():
print('play 1')
time.sleep(3)
print('play 2')
g1 = gevent.spawn(eat)
g2 = gevent.spawn(play)
gevent.joinall([g1, g2])
协程加IO切换操作实现爬虫
from gevent import monkey
monkey.patch_all()
import gevent
import requests
def f1(url):
print('GET: %s' % url)
data = requests.get(url)
print('%d bytes received from %s.' % (len(data.content), url))
def f2(url):
print('GET: %s' % url)
data = requests.get(url)
print('%d bytes received from %s.' % (len(data.content), url))
def f3(url):
print('GET: %s' % url)
data = requests.get(url)
print('%d bytes received from %s.' % (len(data.content), url))
gevent.joinall([
gevent.spawn(f1, 'https://www.python.org/'),
gevent.spawn(f2, 'https://www.yahoo.com/'),
gevent.spawn(f3, 'https://github.com/'),
])
队列
"""
队列:
Queue
redis中的列表
rabbitMQ
"""
from queue import Queue
q = Queue()
"""
q.put('123')
q.put(456)
v1 = q.get()
v2 = q.get()
print(v1,v2)
"""
# 默认阻塞
v1 = q.get()
print(v1)
利用队列实现自动发送邮件操作
"""
1. 申请126或163邮箱
2. 开启服务+授权码
3. 通过代码发送
"""
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
# 写邮件的内容
msg = MIMEText('老板,我想演男一号,你想怎么着都行。', 'plain', 'utf-8')
msg['From'] = formataddr(["炮手", 'zh309603@163.com'])
msg['To'] = formataddr(["老板", '424662508@qq.com'])
msg['Subject'] = "情爱的导演"
server = smtplib.SMTP_SSL("smtp.163.com", 465)
server.login("zh309603", "zhzzhz123") # 授权码
server.sendmail('zh309603@163.com', ['424662508@qq.com', ], msg.as_string())
server.quit()
进程池&线程池
Python2中没有提供线程池,python3之后解释器才提供了线程池.
保证程序中最多可以创建的线程个数,防止无节制的创建线程,导致性能降低.
线程池
"""
import threading
import time
def task():
time.sleep(2)
print('任务')
num = int(input('请输入要执行的任务个数:'))
for i in range(num):
t = threading.Thread(target=task)
t.start()
"""
# ####################### 示例2 ############################
"""
import time
from concurrent.futures import ThreadPoolExecutor
def task(n1, n2):
time.sleep(2)
print('任务')
# 创建线程池
pool = ThreadPoolExecutor(10)
for i in range(100):
pool.submit(task, i, 1)
print('END')
# 等线程池中的任务执行完毕之后,再继续往下走
pool.shutdown(True)
print('其他操作,依赖线程池执行的结果')
"""
# ####################### 示例3 ############################
"""
import time
from concurrent.futures import ThreadPoolExecutor
def task(arg):
time.sleep(2)
print('任务')
return 666
# 创建线程池
pool = ThreadPoolExecutor(10)
ret = pool.map(task,range(1,20))
print('END',ret)
pool.shutdown(True)
for item in ret:
print(item)
"""
# ####################### 示例4 ############################
"""
import time
from concurrent.futures import ThreadPoolExecutor
def task(n1, n2):
time.sleep(2)
print('任务')
return n1+n2
# 创建线程池
pool = ThreadPoolExecutor(10)
future_list = []
for i in range(20):
fu = pool.submit(task, i, 1)
future_list.append(fu)
pool.shutdown(True)
for fu in future_list:
print(fu.result())
"""
进程池
import time
from concurrent.futures import ProcessPoolExecutor
def task(n1, n2):
time.sleep(2)
print('任务')
if __name__ == '__main__':
# 创建线程池
pool = ProcessPoolExecutor(10)
for i in range(20):
pool.submit(task, i, 1)
print('END')
进程线程协程总结***
进程、协程、线程的区别:三个都可以提高并发
进程是计算机中分配资源的最小单位;线程是计算机中CPU调度的最小单位; .
协程又称为“微线程”,是基于代码人为创造的,而进程和线程是计算机中真实
存在的,一个进程中可以有多个线程,一个线程可以创建多个协程
计算密集型,多进程;
I0密集型,多线程/协程+ I0切换
单纯的协程是没办法提高并发,只是代码之间的来回切换,加上I0自动切换才
有意义,有I0操作用协程
并发模块socketserver
import time
from socketserver import BaseRequestHandler,ThreadingTCPServer
# BaseRequestHandler 基础请求操作符
# ThreadingTCPServer 线程实现的基于tcp协议的server
class Myserver(BaseRequestHandler): #继承于BaseRequestHandler
def handle(self): #必须叫handle
n = 0
while True:
self.request.send(str(n).encode('utf-8')) # self.request == conn 等于对方地址
n += 1
time.sleep(0.5)
server = ThreadingTCPServer(('127.0.0.1',9001),Myserver)
#将刚刚定义的数据和绑定地址一起传给ThreadingTCPServer(以tcp协议的形式)
server.serve_forever()
#永远的对外服务为你接受请求之类的一切操作
开发者消费者模型
简单的
import threading
from queue import Queue
import time
q = Queue()
def send(to,subject,text):
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
# 写邮件的内容
msg = MIMEText(text, 'plain', 'utf-8')
msg['From'] = formataddr(["炮手", 'zh309603@163.com'])
msg['To'] = formataddr(["老板", to])
msg['Subject'] = subject
server = smtplib.SMTP_SSL("smtp.163.com", 465)
server.login("zh309603", "zhzzhz123") # 授权码
server.sendmail('zh309603@163.com', [to, ], msg.as_string())
server.quit()
def producer(i):
"""
生产者
:return:
"""
print('生产者往队列中放了10个任务',i)
info = {'to':'424662508@qq.com', 'text':'你好','subject':'好友请求'}
q.put(info)
def consumer():
"""
消费者
:return:
"""
while True:
print('消费者去队列中取了任务')
info = q.get()
print(info)
send(info['to'],info['subject'],info['text'])
for i in range(10):
t = threading.Thread(target=producer,args=(i,))
t.start()
for j in range(5):
t = threading.Thread(target=consumer)
t.start()