软件开放的框架
c/s架构
c就是Client
客户端就是要去请求数据的
s就是Server
服务端就是给客服端根据客户的要求提供数据的
服务端的必备条件
时刻提供服务等待客服端的访问
有一个固定的地址
能够接受多个服务端的请求(高并发)
B/s架构
B就是Browser
就是一个浏览器充当所有服务端的客户端
S就是server
就是一个服务器或服务端
B/S本质还是C/S架构
C/S架构和B/S架构的优势
c/s架构的优势
不同的客服端由不同的人独立开放,可以高度定制化客户端的功能
b/s架构的优势
不需要下载就可以访问不同的服务端
c/s和b/s架构的劣势
c/s架构的劣势
需要下载才能使用服务端
b/s架构的劣势
无法高度定制并且要遵守浏览器的规则才能访问
网络编程的使用
网络编程的介绍
基于网络编写的代码能够实现数据远程的交互
网络编程的作用
用来编写和开发cs架构的软件
网络编程必备的条件
实现数据的远程交互必备的基础条件是物理连接介质
OSL七层协议
osl协议
规定了所有的计算机在远程数据交互的时候必须经过相同的处理流程、在制造过程中必须拥有相同的功能硬件
osl协议的结构
应用层
表示层
会话层
传输层
网络层
数据链路层
物理连接层
背的就是(应、表、会、传、网、数、物)
一般都是整合成四层或者五层
四层架构
应用层
传输层
网络层
网络接口层
五层结构
应用层
传输层
网络层
数据链路层
物理连接层
传输的顺序
接收网络消息
数据从物理连接层到应用层
发送网络消息
数据是由应用层到到物理连接层
物理连接层
主要用于确保计算机之间的物理连接介质接收数据类型(bytes、二进制)
数据链路层
就是规定了电信号的分组方式
规定了计算机在出厂的时候都必要有一串数据在网卡上(数字相当于计算机的身份证号独一无二的)
该数据的形式
十二位16进制的数据(前六位厂商编号 后六位流水线号)或者叫以太网地址/mac地址(确认网络设备位置的位址)
网络相关专业名称
计算机之间要想实现数据交互必须要连接到一起
交互机
能够将所有接入交换机的计算机彼此互联起来
广播
首次查找接入同一个交换机的其他计算机需要朝交换机里去所有的计算机通知一声是那个计算机回应(其它的计算机也知道)
单播
首次被查找的计算机回应查找它的计算机并把自己的mac地址(进行交互)
广播风暴
接入同一台交换机的多台计算机同时进行广播(然后就崩了)
局域网
就是单个交换机组成的网络(在局域网内可以直接使用mac地址通信)
广域网
就是范围更大的局域网
互联网
由所有的的局域网和广域网连接到一起形成的网络
路由器
不同的局域网计算机之间无法直接实现数据的交互就需要路由器
网络层
就是规定了所有接入互联网的计算机都必须有一个IP地址(互联网的)
注意
mac地址是物理地址是永远不能更改的
IP是互联网的地址,不同地方的互联网IP是不同的
IP地址的特性
IPV4
点分十进制
0.0.0.0到
255.255.255.255
IPV6
能够给地球上每一粒沙分一个IP地址(就是很广)
IP地址可以跨局域网传输(IP地址可以用来标识全世界独一无二的一台计算机)
传输层
PORT协议(端口协议)
一个端口同来标识一台计算机上面的某一个应用程序
范围
0到65535
特征
除了个默认的端口,其它端口那个软件先开始就用那个端口
0到1024是系统默认需要使用
1024到8000一些常见的软件端口号
8000之后随便用
UPL
统一资源定位符(网址:就是由IP和PORT组成)
IP+port
就是能够定位全世界独一无二的一台计算机绑定这个端口号的一个应用程序
IP:PORT
实际使用冒号连接(114.55.205.139:80)
传输层与tcp与udp协议
tcp和udp都是用来规定通信方式的
通信的时候可以随心所欲的聊已可以遵循一些协议符合要求的聊
遵循一些协议:开头带尊称首行空两格只准用官话
不遵循上述协议也可以通信 只不过遵循了更合规合法合理!!!
TCP协议
三次握手建链接
客户端向服务端发送建立通信,服务端同意建立通信通道向客户端发送建立通信通道,客户端同意建立通信通道
四次挥手断链接
当客户端没有消息要给服务端发送断开与服务端的通道,服务端确定断开,服务端发送断开与客户端的通信通道,客户端确定跟服务端断开链接
tcp协议也称为可靠协议(数据不容易丢失)
给对方发消息之后会保留一个副本直到对方回应消息收到了才会删除,否则会在一定的时间内反复发送
洪水攻击
同一时间有大量的客户端请求建立链接就是洪水攻击
解决方法
建立半链接池缓冲只能一个一个来
服务端区分客户端建立链接的请求的方法
可以对请求做唯一标识
UDP协议
也称之为数据报协议、不可靠协议
早期的QQ使用的是纯生的(没有加任何额外功能)UDP协议
现在的QQ自己添加了很多技术和功能
使用UDP的原因就是因为很简单,快捷,粗暴只要指定对方的地址就可以发消息了
应用层
就是相当于是程序员自己写的应用程序(里面的协议非常多)
常见的应用层
HTTP、HTTPS、FTP
socket模块
import socket
编写基于网络进行数据交互的程序,socket类似于操作系统封装了代码提供简单快捷的接口
socket代码使用(基础的代码)
注意查看源码看自己需要传什么数据
服务端端
# 1.产生一个socket对象并指定采用的通信版本和协议(TCP)
server = socket.socket() # 括号内不写参数 默认就是TCP协议 family=AF_INET基于网络的套接字 type=SOCK_STREAM流式协议即TCP
# 2.绑定一个固定的地址(服务端必备的条件)
server.bind(('127.0.0.1', 8080)) # 127.0.0.1为本地回环地址 只有自己的电脑可以访问
# 3.设立半连接池(暂且忽略)
server.listen(5)
# 4.等待接客
sock, addr = server.accept() # return sock, addr 三次握手
print(sock, addr) # sock就是双向通道 addr就是客户端地址
# 5.服务客人
data = sock.recv(1024) # 接收客户端发送过来的消息 1024字节
print(data.decode('utf8'))
sock.send('尊敬的客人 您说什么就是什么 一切按照您的要求来'.encode('utf8')) # 给客户端发送消息 注意消息必须是bytes类型
# 6.关闭双向通道
sock.close() # 四次挥手
# 7.关闭服务端
server.close() # 店倒闭了
socket.socket() 产生socket对象
bind():绑定地址
listen(): 半连接池
accept(): 等待客户端链接
send():发送消息
recv():接收消息
connect(): 链接服务端
如果我们需要编写基于网络进行数据交互的程序意味着我们需要自己通过代码来控制我们之前所学习的OSI七层(很繁琐很复杂类似于我们自己编写操作系统)socket类似于操作系统 封装了丑陋复杂的接口提供简单快捷的接口
socket也叫套接字
基于文件类型的套接字家族(单机)
AF_UNIX
基于网络类型的套接字家族(联网)
AF_INET
客户端
import socket
# 1.生成socket对象指定类型和协议
client = socket.socket()
# 2.通过服务端的地址链接服务端
client.connect(('127.0.0.1', 8080))
# 3.直接给服务端发送消息
client.send('大爷有钱 把你们店最好的给我叫出来'.encode('utf8'))
# 4.接收服务端发送过来的消息
data = client.recv(1024)
print(data.decode('utf8'))
# 5.断开与服务端的链接
client.close()
做一下优化(服务端可以跟多个客户交流还可以持续聊天)
服务端
import socket 导入模块
form socket import SOL_SOCKET, SO_REUSEADDR 产生一个socket对象并指定采用什么通信版本和什么协议(默认TCP)
server(变量名) = socket.socket() 采用什么协议
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) 绑定一个固定地址
server.bind((‘127.0.0.1, 8080’))
server.listen(5) 设置等待人数最多最多服务六个客户端(等待五个正在服务一个)
while Ture: 利用循环马上换人
sock(变量名), addr(变量名) = server(变量名).accept() sock就是客服端与服务端的通道,addr就是客户端地址
while True:
try: 捕获异常,可以跟客户断开连接不会报错
data(变量名) = sock.recv(1024) 接收客户端发送来的消息1024字节
if len(data) == 0 : 做一些小判断
break
print(f‘来自于客户端的消息(addr)’, data.decode(‘utf8’))
msg(变量名) = input(‘输入发送个客服端的消息’).strip()
sock.send(msg.encode(‘utf8’)) 发给客户端
except BaseException:
braek
客户端端
import socket
client = socket.socket() 生成协议
client.connect((‘’127.0.0.1‘’, 8080)) 连接服务端
while True: 实现客服端与服务端一直交互
msg(变量名) = input(‘输入发给服务端的消息’).strip()
if len(mag) == 0: 做一些小判断
print(‘空消息’)
接收服务端的消息
client.send(mas.encode(‘utf8’))
data = client.recv(1024)
print(‘来自服务端的消息’, data.decode(‘utf8’))
半链接池
server.listen(设置数量):半连接池
当有多个客户端来链接的情况下我们可以设置等待数量(不考虑并发问题),开始链接一个可以等待设置数据,超过报错不行
黏包现象(不知道数据多大或者连续发了几天黏到一起)
使用struct模块将数据打包成固定的长度
import struct 模块导用
y(变量名) = b‘数据’
s = struct.pack(‘i’, len(y))将数据打包
这样就把一个数据打包成一个固定的大小(注意取值的时候索引0元组)
解析真实的长度
z(变量名) = struct.unpack(‘i’, s)
打包字典
s(变量名) = json.dumps(字典数据)
z(变量名) = sturct.pack(‘i’, len(s.encode(‘utf8’)))
解决方法
客户端
1.制作真实数据的信息字典(数据长度、数据简介、数据名称)
2.利用struct模块制作字典的报头
3.发送固定长度的报头(解析出来是字典的长度)
4.发送字典数据
5.发送真实数据
服务端
1.接收固定长度的字典报头
2.解析出字典的长度并接收
3.通过字典获取到真实数据的各项信息
4.接收真实数据长度
UDP协议(了解)
1.UDP服务端和客户端'各自玩各自的'
2.UDP不会出现多个消息发送合并
可以和多个客户聊天按循序,不过不管你有没有接收到数据(数据容易丢失)
服务端
import socket
server = socket.socket(type=socket.SOCK_DGRAM) UDP协议
server.bind((‘127.0.0.1’,8080))
while True:
data, addr = server.recvfrom(1024)
print('客户端地址', addr)
print(‘客户端发送来的消息’, data.decode(‘utf8’))
msg = input(‘往客户端发送消息’).strip()
server.sendto(msg.encode(‘utf8’),addr)
客户端
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_addr = ('127.0.0.1', 8080)
while Ture:
msg = input('发给服务端的消息')
client.sendto(msg.encode(‘utf8’), server_addr) 发给服务端
data, addr = client.revfrom(1024)
print(data.decode('utf8'), addr) 接收服务端的消息
并发编程
计算机中真正干活的是CPU
计算机很庞大使用很麻烦一次只能给一个人使用期间很多时候计算机都不工作
好处:程序员独占计算机 为所欲为
坏处:计算机利用率太低浪费资源
联机批处理系统
前使用磁带一次性录入多个程序员编写的程序,然后交给计算机执行,CPU工作效率有所提升,不用反复等待程序录入提前
脱机批处理系统
极大地提升了CPU的利用率
多道技术
单道技术
所有的程序排队执行,过程中不能重合就是上面一条走到底
多道技术
利用空闲时间提前准备其他数据最大化提升CPU利用率
多道技术详细
切换
计算机的CPU在两种情况下会切换(不让你用 给别人用)
程序有IO操作的时候切换
输入\输出操作:input、time.sleep、read、write那么cpu就会去执行别的程序
程序长时间占用CPU
我们得雨露均沾让多个程序都能被CPU运行一下只要那个程序没怎么用就会执行别的程序等你要使用了再去
保存状态
CPU每次切换走之前都需要保存当前操作的状态,下次切换回来基于上次的进度继续执行
进程理论
进程与程序的区别
程序:一堆死代码(还没有被运行起来的程序)
进程:正在运行的程序(被运行起来的程序)
进程的调度使用(重要)
FCFS(先来先服务)
对短作业不友好
短作业优先调度
对长作业不友好
时间片轮转法+多级反馈队列(目前还在用)
将时间均分给同时启动的进程,然后根据进程时间长短再分多个等级,等级越靠下表示耗时越长每次分到的时间越多但是优先级越低,在运行新的进程会优先执行它
进程的并行与并发(追求高并发)
并行
多个进程同时执行必须要有多个CPU参与单个CPU无法实现并行
并发
多个进程看上去像同时执行单个CPU可以实现多个CPU肯定也可以
进程的三状态
就绪态
所有的进程在被CPU执行之前都必须先进入就绪态等待
运行态
CPU正在执行
阻塞态
进程运行过程中出现了IO操作 阻塞态无法直接进入运行态 需要先进入就绪态,想压榨cpu最后就是一直处于运行态和就绪态来回切换,游戏就是
同步与异步的了解
同步
提交完任务之后原地等待任务的返回结果期间不做任何事就是等待返回结果
异步
提交完任务之后不愿地等待任务的返回结果直接去做其他事有结果自动返回通知
阻塞
就是在阻塞态
非阻塞
就是在就绪态和运行态来回切换
综合使用(异步非阻塞最好)
同步阻塞,同步非阻塞,异步阻塞,异步非阻塞:效率最高(游戏就是使用这种让系统不能停)
创建进程的多种方式
注意不同操作系统创建的底层原理
windows:以导入模块的形式创建进程
mac\linux:以拷贝代码的形式创建进程
创建多进程
鼠标双击软件图标(让程序运行起来)太low
python代码创建进程,使用模块multiprocessing
同步创建
from multiprocessing import Procese 导入模块
import time
def task(name):
print(‘task’,name)
time.sleep(3) 三秒后执行
print(‘tasks’,name)
if __name__ == '__main__':
p1(变量名) = Process(target=task(函数名),args=(‘jason’,))传参调用函数
task(‘你好’)
print(‘主’)
结果就是:task你好,tasks你好,主
异步创建
from multiprocessing import Process
import time
def task(name):
print('task', name)
time.sleep(3)
print('tasks', name)
if __name__ == '__main__':
p1 = Process(target=task, args=('jason',))
p1.start()
print('主')
结果是:主,ask jason,asks jason
用类的方法创建(异步创建)
from multiprocessing import Process
import time
class MyProcess(Process):
def __init__(self, name, age):
super().__init__()
self.name = name
self.age = age
def run(self):
print('running', self.name, self.age)
time.sleep(3)
print('over', self.name, self.age)
if __name__ == '__main__':
obj = MyProcess('jason', 1233)
obj.start()
print('主')
进程间数据隔离
同一台计算机上的多个进程数据是严格意义上的物理隔离(默认情况下)
进程的join方法
注意join放置的位置,有不同的效果
放在一起会同时运行放在一起会同时运行
from multiprocessing import Process
import time
def task(name, n):
print('task', name)
time.sleep(n)
print('tasks', name)
if __name__ == '__main__':
p1 = Process(target=task, args=('jason1', 1))
p2 = Process(target=task, args=('jason2', 2))
p3 = Process(target=task, args=('jason3', 3))
p1.start()
p2.start()
p3.start()
p1.join()
p3.join()
p2.join()
放在每个代码下面就是同步慢慢等待(等待上面的结束才走下一步)
from multiprocessing import Process
import time
def task(name, n):
print('task', name)
time.sleep(n)
print('tasks', name)
if __name__ == '__main__':
p1 = Process(target=task, args=('jason1', 1))
p2 = Process(target=task, args=('jason2', 2))
p3 = Process(target=task, args=('jason3', 3))
p1.start()
p1.join()
p2.start()
p2.join()
p3.start()
p3.join()
IPC机制(进程之间交互存储和拿取)
模块的导用
from multiprocessing import Queue
q(变量名) = Queue(能储存几个数据)
.put:往消息队列中存放数据
.get():从消息队列中取出数据
q.full():判断队列满了没有
q.empty():判断队列是否为空
full() empty() 在多进程中都不能使用多进程可以判断完后在存数据或者拿数据(注意超出范围会一直卡在哪里会等待数据的拿出或者存入到指定的数量才会运行)
进程之间的通信
查看进程号
tasklist 在cmd窗口运行查看所以进程
还有一些进程小知识点
https://www.cnblogs.com/yinjinxi/p/17270342.html
多进程实现TCP服务端并发
进程多个服务端可以同时跟多个客户端聊天
服务端
import socket
from multiprocessing import Process
def get_s():
s = socket.socket()
s.bind(('127.0.0.1', 8080))
s.listen(5)
return s
def get_yy(sock):
while True: # 接收客户端的消息
z = sock.recv(1024)
print(z.decode('utf8'))
sock.send(z.upper())
if __name__ == '__main__':
s = get_s()
while True:
sock, addr = s.accept()
p = Process(target=get_yy, args=(sock,))
p.start()
客户端
import socket
ss = socket.socket()
ss.connect(('127.0.0.1', 8080))
while True:
ss.send(b'shuju')
z = ss.recv(1024)
print(z)
互斥锁
锁:建议只加载操作数据的部分否则整个程序的效率会极低
模块导入
from multiprocessing import Process, Lock
Lock就是锁
注意只有拿到锁的进程结束把锁释放出来才会进行下一个进程
线程理论
进程
进程其实是资源单位表示一块内存空间
线程
线程是执行单位是真正的代码指令
注意事项
一个进程内部至少含有一个线程
一个进程内可以开设多个线程
同一个进程下的多个线程数据是共享的
创建进程与线程的区别
创建进程的消耗要远远大于线程,进程能利用多核优势的,线程就不能利用多核优势但是,可以开启多个进程,在进程里面在开启线程,进程之间的数据隔离的,线程之间的数据是不隔离的,前提:同一进程之间
创建线程
模块导入
from threading import Thread
线程很快的跟看打印结果就知道,注意:if name == 'main':这个可用可不用 用下也可以
用类的方法创建
线程的诸多特性
join方法
等join上面代码运行完在运行下面代码
同进程内多个线程数据共享
查看线程的名字current_thread()
属于同一个进程
查看线程能用的数量active_count()
注意使用场景放在代码结束后最会是最后一个进程的线程
线程的守护跟进程一样
当主线程运行结束,子线程会跟着结束
GIL全局解释器锁
验证GIL的存在(了解)
创建这个函数确定gtl存在每次都在减一而不是99
GIL与普通互斥锁
GIL只能够确保同进程内多线程数据不会被垃圾回收机制弄乱
并不能确保程序里面的数据是否安全,加个阻塞验证一下
这样数据就乱了该要的锁还是要锁
python多线程
单个CPU,IO密集型(多线程就有优势)
多进程:申请额外的空间消耗更多的资源
多线程:消耗资源相对较少通过多道技术
计算密集型(多线程有优势)
多进程:申请额外的空间 消耗更多的资源(总耗时+申请空间+拷贝代码+切换)
多线程:消耗资源相对较少 通过多道技术(总耗时+切换)
多个CPU
IO密集型(多线程有优势)
多进程:总耗时(单个进程的耗时+IO+申请空间+拷贝代码)
多线程:总耗时(单个进程的耗时+IO)
计算密集型(多进程就好多了)
多进程:总耗时(单个进程的耗时)
多线程:总耗时(多个进程的综合)
死锁现象(就是多线程或者多进程拿多个用样的锁一个要一个人锁,另一个人也要锁一个人的锁导致程序直接卡注。)
信号量(就是产生多把锁,变量名 = Semaphore(5)数字几就是产生几把锁,在python并发编程中信号量相当于多把互斥锁(公共厕所))
event事件(给你个信号什么走就什么时候走‘红绿灯’)
进程池与线程池(限制进程与线程的数量)
进程池:提前创建好固定数量的进程供后续程序的调用超出则等待
线程池:前创建好固定数量的线程供后续程序的调用超出则等待
协程
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
concurrent.futures:模块名
ProcessPoolExecutor:进程
ThreadPoolExecutor:线程
变量名 = ThreadPoolExecutor(10)
数字多少就是开设多少线程
pool = ProcessPoolExecutor(5)开设进程池数量多少就是多少进程
协程
单线程下实现并发(效率极高)在代码层面欺骗CPU 让CPU觉得我们的代码里面没有IO操作实际上IO操作被我们自己写的代码检测 一旦有 立刻让代码执行别的
核心:自己写代码完成切换+保存状态
协程实现并发
import socket
from gevent import monkey;monkey.patch_all() # 固定编写 用于检测所有的IO操作(猴子补丁)
from gevent import spawn
def communication(sock):
while True:
data = sock.recv(1024)
print(data.decode('utf8'))
sock.send(data.upper()
def get_server():
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
sock, addr = server.accept() # IO操作
spawn(communication, sock)
s1 = spawn(get_server)
s1.join()