目录
网络编程
网络起源 (了解)
网络目的 : 数据的传输
ISO : 国际标准化组织
OSI七层模型 ----》 网络通信的标准化流程
应用层 : 提供用户服务,具体的内容由特定程序规定
表示层 : 数据的压缩优化加密
会话层 : 建立应用连接,选择传输层服务
传输层 : 提供数据传输服务,流量控制
网络层 : 路由选择,网络互连
链路层 : 提供链路交换,具体的消息的发送
物理层 : 物理硬件,接口 网卡的规定
osi模型优点 : 将功能分开,降低网络传输中的耦合度每一部分完成自己的功能,开发更加清晰专一
四层模型
应用层 : 应用层 表示层 会话层
传输层 : 传输层
网络层 : 网络层
物理链路层: 链路层 物理层
五层模型(tcp/ip模型)
应用层 : 应用层 表示层 会话层
传输层 : 传输层
网络层 : 网络层
链路层 : 链路层
物理层 : 物理层
要求 : 能够按顺序说出七层模型 五层和四层模型
知道每一层是什么作用
对网络消息传输流程有基本了解
协议 :网络通信中,各方必须遵守的规定。包括建立什么样的连接,消息结构等。
应用层 : TFTP HTTP DNS SMTP
传输层 : TCP UDP
网络层 : IP
物理层 : IEEE
网络相关概念
主机 : host 表示一台计算机
本地使用 : 'localhost'
''
'127.0.0.1'
网络使用 : '0.0.0.0'
'172.60.50.60'
获取计算机名:
In [2]: socket.gethostname()
Out[2]: 'tedu'
通过计算机名获取地址:
In [3]: socket.gethostbyname('tedu')
Out[3]: '127.0.1.1'
IP地址
在网络上确定一台主机的地址
IPv4 :点分十进制 比如 192.168.1.72 0--255
32位二级制表示
IPv6 : 128位二进制
windows cmd.exe 窗口 --》 ipconfig
linux 查看本机地址 ifconfig
网络连接测试
ping 172.60.50.60
特殊IP
127.0.0.1 本地测试IP
0.0.0.0 使用局域网内可用的IP
192.168.1.0 表示当前网段
192.168.1.1 通常是网络节点设备的IP (网关)
192.168.1.255 广播地址
通过域名获取服务器信息
socket.gethostbyaddr('www.baidu.com')
('127.0.0.1', [], ['119.75.213.61'])
主机 主机别名 主机IP
将点分十进制IP转换为二进制
In [11]: socket.inet_aton('192.168.1.2')
Out[11]: b'\xc0\xa8\x01\x02'
将二进制转换为点分十进制IP
In [12]: socket.inet_ntoa(b'\xc0\xa8\x01\x02')
Out[12]: '192.168.1.2'
功能 : 同上面两个完全相同。只是可以选择地址类型
inet_pton(socket.AF_INET,'192.168.1.2')
inet_ntop()
域名: 网络服务器地址的网络名称
端口号 : 端口号是网络地址的一部分,在一个系统中 每个网络应用都会有一个端口号相对应。用来在接收消息时确定由谁来接收
范围 : 1--65535
1--255 一些通用的众所周知程序占用
256-1023 系统应用端口
1024 --- 65535 自用 >10000
获取应用端口:
In [16]: socket.getservbyname('mysql')
Out[16]: 3306
字节序
数据在内存中的存储解析方式,不同操作系统可能不同
网络字节序 : 不同的操作系统在消息收发时均按照该标准操作。
传输层服务
面向连接的传输服务 ----》 tcp协议
传输特征:
可靠的数据传输
可靠性指数据传输中 无失序 无差错 无丢失 无重复
所有消息传输前一定会建立连接,传输后一定会断开连接
三次握手: 在进行面向连接的数据传输前进行传输连接的过程
1. 客户端向服务器发送连接请求(问是否可以连接)
2. 服务器接收到连接请求进行确认,返回报文
3. 客户端收到回复,进行连接建立
四次挥手: 在进行面向连接的数据传输时,断开连接的过程
1. 主动方发送报文 告知被动方要断开连接
2. 被动方返回报文,告知收到请求,准备断开
3. 被动方再次发送报文给主动方,告知准备完毕可以断开
4. 主动方发送报文进行断开
应用情况 :适用于传输较大的内容或文件,网络情况良好,需要保证传输可靠性的情况
比如 : 聊天信息,文件的上传下载,邮件传输
网页获取
面向无连接的传输服务 ---》 udp协议
传输特点:
不保证可靠的数据传输
没有连接过程
数据的收发都比较自由,不会受另一端制约
适用情况: 网络情况较差,对传输可靠情形要求不高。需要提升传输效率的情况
比如 : 网络视频,群聊,发送广播
问题总结:
1.osi模型问题
2.三次握手四次挥手问题
3.tcp和udp的区别
socket套接字编程
目的 : 通过编程语言提供的函数接口进行组合,更简单的完成基于 tcp 或者udp通信的网路编程
套接字 : 完成上述目标的一种编程方法
套接字分类
流式套接字(SOCK_STREAM): 传输层基于tcp的协议进行通信
数据报套接字 (SOCK_DGRAM):传输层基于udp协议进行通信
底层套接字 (SOCK_RAM):访问底层协议的套接字
TCP服务端
import socket
1. 创建套接字
socket.socket(socket_family = AF_INET,socket_type = SOCK_STREAM,proto = 0)
功能 : 创建一个套接字
参数 : socket_family : 选择地址族类型 AF_INET
socket_type :套接字类型 SOCK_STREAM 流式
SOCK_DGRAM 数据报
proto :子协议类型,没有子协议选择 0
返回值 : 套接字对象
2. 绑定地址(IP 端口号)
sockfd.bind()
功能 : 绑定地址
参数 : 一个元组包含两项
第一项为IP第二项为端口号
('172.60.50.50',8888)
3. 将套接字设置可监听
sockfd.listen(n)
功能 : 将套接字设置为监听套接字
参数 : n是一个正整数 表示监听等待队列的大小
4. 等待接收连接请求
connfd,addr = sockfd.accept()
功能: 阻塞等待处理客户端连接
返回值 : 第一个 : 一个新的套接字用来和客户端通 信
第二个 : 连接的客户端的地址
* 阻塞函数:程序运行到阻塞函数的位置,如果某种期待条件没有达成则暂停继续运行。当条件达成后会结束阻塞继续运行。
5. 收发消息
data = connfd.recv(buffersize)
功能 : 流式套接字接收消息
参数 : 一次最多接收多大的消息 字节
返回值 : 返回接收到的内容
n = connfd.send(data)
功能 : 流式套接字发送消息
参数 : 要发送的内容 要求bytes格式
返回值 : 返回实际发送的字节数
6. 关闭套接字
close()
功能:关闭套接字 tcp套接字连接断开
tcp 客户端
1. 创建套接字
socket()
2. 请求连接
connect()
功能: 发起连接请求
参数: 是一个元组,表示服务器的地址
3. 消息收发
recv() send()
4. 关闭套接字
close()
作业 :
1. 口述 什么是七层模型,tcp/ip模型
三次握手和四次挥手的过程
tcp协议的传输和udp协议的传输有何区别
2. 将tcp程序改写为一个通过循环可连续收发消息的程序
day02
recv() 特征
*如果连接的另一端断开连接,则recv立即返回空子串
*recv是从接收缓冲区取出内容,当缓冲区为空则阻塞
*如果一次接收不完缓冲区内容,下次会继续接收
send() 特征
*如果发送的另外一端不存在则会产生Pipe Broken
*send是向缓冲区发送内容,当缓冲区为满阻塞
网络的收发缓冲区
在内存中开辟的区域,用于发送和接受的缓冲
*协调数据收发速度
*减少和磁盘的交互
sendall()
功能:tcp套接字发送消息
参数:同send
返回值:如果发送成功返回None,否则产生异常
tcp粘包
产生原理:
*tcp传输以字节流的方式发送消息,消息之间没有边界
*发送比接受的速度快
影响:对每次发送的内容是一个独立的意思需要单独识别
如何处理粘包:
1.每次发送后加一个结尾标志
2.发送一个数据结构
3.每次放有一个间隔
基于UPD的服务端编程
1.创建套接字
sockfd = socket(AF_INET, SOCK_DGRAM)
2.绑定地址
sockfd.bind()
3.消息收发
data,addr = sockfd.recvfrom(buffersize)
功能:接收UDP消息
参数:每次最多接收下次的大小 字节
返回值:data 接收到的内容
addr 消息发送方的地址
sockfd.sendto(data,addr)
功能:UDP消息发送
参数:data 要发送的内容 bytes
addr 目标地址
返回值:发送多少字节
4.关闭套接字
sockfd.close()
cookie:
import sys
sys.argv
*recvfrom每次接收一个报文,如果没有接收完全则丢弃没有收到的内容
tcp流式套接字和udp数据报套接字区别
1.流式套接字采用字节流方式进行传输,而数据报套接字使用数据报形式传输数据
2.tcp套接字会产生粘包,udp不会
3.tcp编程可以保证消息的完整性,udp则不能保证
4.tcp需要listen accept,udp不用
5.tcp消息的发送接收使用recv,send,sendall,udp使用recvfrom,sendto
socket模块和套接字属性方法
属性
s.type :套接字类型
s.family :回去地址族类型
方法
s.fileno()
功能:获取套接字的文件描述符
文件描述符:每一个IO事件操作系统都会分配一个不同的正整数与之匹配,该整数即为此IO的文件描述符
eg:
sys.stdin 0
sys.stdout 1
sys.stderr 2
s.getsockname()
功能:获取套接字绑定的地址,未绑定默认('0.0.0.0',0)
返回值:IP元组
s.getpeername()
功能:获取连接套接字客户端的地址
s.setsockopt(level,optname,value)
功能:设置套接字选项
参数:level 设置选项类型 IPPROTO_* SOL_*
optname 子类选项
value
s.getsockopt(level,optname)
功能:获取套接字选项值
参数:level 设置选项类型,optname 子类选项
返回值:获取值
udp套接字应用 ---> 广播(broadcast)
一点发送多点接收
特殊目标地址:广播地址 176.140.18.255
cookie:
'..{}..{:^10}..'.format('...','...')
tcp ---> http传输
http协议 --> 超文本传输协议
用途:网站中浏览器获取网页的过程
编写基于http协议的数据传输
特点:
1.应用程序协议,传输层使用TCP协议
2.简单,灵活,可以使用多种编程语言操作
3.无状态的协议,即不记录用户输入的内容
4.http1.1 ---> http2.0(还在研发) 技术成熟和稳定
http请求(request)
请求格式:
请求行
提供具体的请求类别和请求内容
GET /index.html HTTP/1.1
请求类别 请求内容 协议版本
请求类别:GET 获取网络资源
POST 提交一定的附加数据
HEAD 获取响应头
PUT 更新服务器资源
DELETE 删除服务器资源
CONNECT 预留请求
TRACE 用于测试
OPTIONS 获取服务器性能信息
请求头
对请求的具体描述(键值对方式)
Accept:text/html
*每一个键值占一行,描述了一个特定信息
空行
请求体
具体的参数或者提交的内容
get参数或者post提交的内容
http响应(response)
响应格式:
响应行
反馈具体的响应情况
HTTP/1.1 200 OK
协议版本 响应码 附加信息
响应码: 1xx 提示信息
2xx 响应成功
3xx 响应需要重定向
4xx 客户端错误
5xx 服务器错误
常见: 200 访问成功
404 请求不存在
401 没有访问权限
500 服务器发生未知错误
503 服务器暂时无法执行
响应头
对响应内容的具体描述(键值对方式)
Content-Encoding:gzip
Content-Type:text/html
空行
响应体
将客户端请求内容进行返回
要求:
知道http作用
掌握http协议 请求和响应的格式以及每一部分
掌握http协议中请求的基本类型和作用
知道http响应码的类型和表达含义
了解通过http协议请求网页的流程
day03
tcp应用之 httpserver
*接收http请求
*查看http请求
*返回一个网页给客户端
IO input output
在内存中存在数据交换的操作可以认为是IO操作(输入输出)
内存和磁盘进行数据交换->文件读写 数据库更新
内存和终端数据交换->input print sys.stdin sys.stdout sys.stderr
内存和网络数据的交互->网络连接 recv send recvfrom sendto
IO密集型程序
在程序执行中有大量的IO操作,而较少的cpu运算操作
消耗cpu较少,IO运行时间长
CPU(计算)密集型程序
程序中存在大量的cpu运算,IO操作相对较少,消耗cpu大
IO分类
阻塞IO 非阻塞IO IO多路复用 事件驱动IO 异步IO
阻塞IO:程序运行中遇到IO条件没有达成,或者传输过程中较慢的情况会出现阻塞状态
* 阻塞IO是 IO最简单的逻辑情形,也是默认状态
* 阻塞IO是效率很低的IO形态
阻塞情况:
* 因为某种条件没有达到形成阻塞
e.g. accept recv recvfrom input
* 处理IO事件的事件较长形成的阻塞
e.g. 文件读写 网络数据发送过程
非阻塞IO:在程序运行中遇到IO的情形不让其产生阻塞
实现手段:改变IO事件的属性,使其变为非阻塞
* 通常会和循环一起使用,进行条件的循环监控
s.setblocking(False)
功能:将套接字设置为非阻塞状态
参数:默认为阻塞状态 设置为False则为非阻塞
超时检测
将原本阻塞的函数,设置一个阻塞的最长时间,在时间内如果任然阻塞则不再等待
s.settimeout(sec)
功能:设置套接字的超时时间
参数:时间(s)
IO多路复用
定义:通过一个监测,可以同时监控多个IO事件的行为。当哪个IO事件可以执行,即让这个IO事件发生
IO事件就绪:IO事件即将发生的临界状态
* 在程序中存在的IO事件中选择要监测的
* 创建监测:将监测的事件注册
* 等待监测的IO事件发生,判断是什么IO事件
* 处理相应的IO
import select
select ---> Windows linux Unix
poll ---> linux Unix
epoll ---> linux Unix
rs,ws,xs = select(rlist,wlist,xlist[,timeout])
功能:监控IO事件,阻塞等待监控的IO事件发生
参数:rlist 列表 存放需要等待处理的IO
wlist 列表 存放想主动处理的IO
xlist 列表 存放出错希望去处理的IO
timeout 超时检测
返回值:rs 列表 rlist中准备就绪的IO
ws 列表 wlist中准备就绪的IO
xs 列表 xlist中准备就绪的IO
* 在处理IO时不要形成死循环,让一个客户端单独占有服务端
* IO多路复用形成一种可以同时处理多个IO的效果,效率较高
位运算
按照二进制位进行运算操作
& 与
| 或
^ 异或
<< 左移 右侧补0
>> 右移 挤掉右侧数字
使用:
在底层硬件时操作寄存器
做标志位的过滤
poll 方法实现IO多路复用
1.创建poll对象
p = select.poll()
2.注册关注的IO
p.register(s, POLLIN | POLLHUP)
p.unregister(s)
事件类别
POLLIN rlist
POLLOUT wlist
POLLERR xlist
POLLHUP 断开
POLLPRI 紧急处理
3.监控IO
events = p.poll()
功能:监控关注的IO事件
返回值:返回发生的IO事件
events 时一个列表 [(fileno,event1),(fileno,event2),...]
每个就绪IO对应列表中的一个元组(描述符,就绪事件)
IO地图:{s.fileno():s} 需要不断维护
4.处理IO事件
作业:
1.熟练select_server.py代码
2.了解 IO多路复用原理机制
3.写一个 select 服务端,同时关注客户端连接,客户端输入和终端输入(sys.stdin)。将客户端和终端输入的内容,全都写入到一个文件中
day04
epoll
使用方法:代码基本与poll一致
* 将生成的对象改为 p = epoll()
* 将事件类别名称改为epoll关注事件类别
区别
epoll:效率要高于select 和 poll
epoll(内核决定返回就绪的IO)
select/poll(内核将所有IO全部返回,应用层决定哪个就绪)
epoll 的触发机制更多 --> EPOLLET(边缘触发)
水平触发:就绪未处理IO不断轮询
边缘触发:当有IO未处理时,不在处理,当下次其他IO需要处理时,一起处理
本地套接字
cookie
linux下文件类型
b 块设备文件(驱动)
c 字符驱动设备文件(鼠标,键盘)
d 目录
- 普通文件(文本视频压缩包)
l 链接文件
s 套接字文件
p 管道文件
本地套接字作用:
用于本地不同程序间,进行数据传输
1.创建套接字
sockfd = socket(AF_UNIX, SOCK_STREAM)
2.绑定套接字文件,如果文件不存在则自动创建
sockfd.bind(file)
3.监听
listen
4.消息收发
recv send
cookie
os.path.exists(file)
功能:判断一个文件是否存在
参数:文件
返回值:True / False
os.remove()
os.unlink()
功能:删除一个文件
参数:要删除的文件
网络基础总结:
1.OSI七层模型 TCP/IP模型
2.TCP和UDP区别
3.三次握手和四次挥手过程和作用
4.什么是IO多路复用,IO的基本形态
5.套接字的种类,套接字类型的区别
程序实践:
1.tcp套接字传输的基本模型
2.udp套接字传输的基本模型
3.IO多路复用select poll 使用
4.http协议基本原理的实现
***********************************************
多任务编程
意义:充分利用计算机资源,同时运行多个任务,提高程序整体的执行效率
定义:通过程序利用计算机的多个核心达到同时执行多个任务的目的。一次达到提升程序效率的目的
实施方案:
多进程编程
多线程编程
并行:
多个计算机核心在同时处理多个任务,这是多个任务之间是并行关系
并发:
同时处理运行多个任务,内核在多个任务间不断切换达到多个任务都会被执行的处理效果,此时多个任务间是并发关系
同一个计算机内核在同一时刻只能运行一个程序
进程:(process)
程序在计算机中的一次执行过程
进程是一个动态的过程,占有计算机资源,有一定的生命周期
程序:
程序是一个可执行文件,是静态的,占有磁盘,不占有计算机资源
* 同一个程序不同的运行过程是不同的进程。因为分配的资源和生命周期都不相同
进程的创建流程
1.用户启动一个程序或者调用接口发起进程的创建
2.操作系统接收用户请求,分配计算机资源,创建进程
3.操作系统将一定状态的进程提供给用户使用
4.用户利用操作系统提供的进程完成任务
CPU时间片
如果一个进程占有cpu,此时我们称为该进程占有cpu的时间片。多个进程任务会轮流占有cpu时间片形成并发效果。
进程信息
PCB (进程控制块)
进程创建后会自动在内存产生一个空间存放进程信息
进程信息:进程的ID 进程占有内存的位置 创建时间 创建用户
查看系统该进程信息:ps -aux
PID(process ID):在操作系统中每个进程都有唯一的PID值是由系统分配的。
进程特征:
* 进程是操作系统分配资源的最小单元
* 每个进程拥有自己独立的运行空间(虚拟内存空间)
* 进程之间运行相互独立互不影响
进程的状态:
三态
* 就绪态:进程准备执行条件,等待系统分配处理器资源进入运行态
* 运行态:进程占有cpu处于运行态
* 等待态:进程暂时不具备运行条件,需要阻塞等待
五态
在三态基础上增加了 新建 和 终止
新建:创建一个新的进程,获取系统资源的过程
终止:进程执行结束,释放资源的过程
STAT表示进程状态
D 等待态 阻塞 不可中断等待态
S 等待态 睡眠 可中断等待态
T 等待态 暂停 暂停执行
R 运行态(就绪态)
Z 僵尸
+ 前台进程(在终端运行)
< 有较高优先级的进程
N 较低优先级进程
s 会话组
l 有进程连接
top命令(查看进程状态-动态)
< > 翻页 q 退出
nice 命令 优先级(-20~19) 最高-20
以指定优先级运行一个程序
e.g.
nice -9 ./while.py (以9的优先级运行程序)
sudo nice --9 ./while.py (以-9优先级运行)
cookie
首行添加
#! /usr/bin/python3
修改程序程序添加可执行权限
chmod 777 while.py
可以直接执行
./while.py
父子进程
在系统中处理初始化进程每个进程都已一个父进程,可能有0个或多个子进程。由此形成进程间的父子关系。
便于进程管理,父进程发起创建子进程请求
查看进程树:pstree
查看父子进程PID:ps -aux
要求:
1.什么是进程,进程和程序的区别
2.了解进程的特征和基本区别
3.理解并发和并行的区别
4.清除进程的状态转换
os.fork 创建进程
pid = os.fork()
功能:创建进程
返回值:失败返回一个负数
成功:在原有进中返回新进程的pid号,在新进程中返回0
* 子进程会复制父进程全部的内存空间包括代码段
* 子进程会从fork的下一句开始执行
* 父进程中fork返回值即为新创建子进程的pid号
* 父子进程不一定谁先执行,执行上互不干扰抢占时间片
* 使用if 语句结构使父子进程执行不同的代码几乎是fork创建进程的固定结构
* 在子进程中对变量等其他内容的修改,不会影响父进程中的内容
* 子进程虽然复制父进程内容,但是也有自己的特有属性特征。比如PID号 PCB内存区间等
获取进程PID
os.getpid()
功能:获取进程的PID号
返回值:返回进程的PID号
os.getppid()
功能:获取 父进程 的PID号
返回值:返回 父进程 的PID号
进程退出
os._exit(status)
功能:退出一个进程
参数:进程的退出状态(整数 必选)
sys.exit([status])
功能:退出一个进程
参数:默认为0
如果传入一个整数则同 _exit(n)
传入一个字符串,则在退出时打印该字符串
* sys.exit() 是通过异常使程序退出,可以用try语句捕获(SystemExit),退出原因是[status]
* os._exit() 退出不可逆
作业:
1.理解进程fork
2.熟悉进程的理论内容
day05
孤儿进程:
当父进程先于子进程退出时,子进程就会变成孤儿进程
* 孤儿进程会被系统指定进程收养,即系统进程会成为孤儿进程新的父进程。系统进程会自动处理孤儿进程退出状态
僵尸进程:
子进程先于父进程退出,父进程没有处理子进程的退出状态,此时子进程就会成为僵尸进程
* 僵尸进程会滞留部分PCB信息在内存中,大量的僵尸进程会消耗系统给的内存资源,所以要尽量避免僵尸进程的产生
如何避免僵尸进程产生
* 父进程先退出
* 父进程处理子进程退出状态
pid, status = os.wait()
功能:在父进程中阻塞等待子进程的退出
返回值:pid 退出的那个子进程PID号
status 子进程的退出状态
获取原理退出状态
os.WEXITSTATUS(status)
pid, status = os.waitpid(pid, option)
功能:在父进程中阻塞等待子进程的退出
参数:pid -1 表示等待任意子进程退出
>0 表示等待对应PID号的子进程退出
option 0 表示阻塞等待
WNOHONG 表示非阻塞
返回值:pid 退出的那个子进程PID号
status 子进程的退出状态
创建二级子进程
父进程创建子进程等待子进程退出
子进程创建下一级子进程,然后立即退出
二级子进程成为孤儿进程,处理具体工作
类似于qq群聊
1.进入聊天室,需要输入姓名,且不能重复
2.有人进入聊天室此时会向其他人发起通知
xxx 进入了聊天室
3.如果一个人发信息,则其他人都能收到
xxx 说: xxx
4.如果某人退出聊天室,其他人也会收到通知
xxx 退出了聊天室
5.服务端可以喊话,此时群里所有人都能收到服务端消息
管理员 说:xxx
整体结构:
如何封装
使用什么样的技术手段
服务端 客户端
在客户端和服务端每个功能封装为一个函数
技术方案
转发:一个客户端发送给服务器,服务器发送给其他人
套接字使用 udp 完成操作
用户存储:列表 字典(可变类型,能够遍历提取)
地址,用户名
发送和接收消息的控制:发送和接收使用多进程分离互不影响
注意事项:
1.注重封装
2.分段测试
代码编写流程
先搭建通信
创建多进程
每个进程功能确定
实现每个功能模块
功能细节梳理
进入聊天室
客户端:创建用户名
将信息发送给服务器
接收到服务器返回结果判断下一步执行什么
服务端:接收消息
判断请求类型
判断是否可以登录(判断姓名是否已经存在)
返回给客户端是否登录(如果可以服务端会将姓名插入到存储结构)
给所有人发送消息
聊天
客户端
创建父子进程收发消息
向客户端发送 聊天消息+用户名+请求类型
接收服务器回复
接收服务端发来的 聊天消息
服务端
创建父子进程收发消息
接收客户端发送 聊天消息+用户名信息+请求类型
以 用户名+聊天消息方式 转发给其他人
退出聊天室
客户端
客户端发送 退出信息+用户名
接受回复
退出程序
服务端
接收客户端发送 退出信息+用户名
删除已退出用户信息,并向所有人发送 xxx已退出
multiprocessing 模块创建进程(标准库模块)
1.需要将要做的事情封装成函数
2.使用multiprocessing提供的Process类创建进程对象
3.通过进程对象和Process初始化进程进行进程的设置,绑定函数
4.启动进程,会自动执行绑定的函数
5.完成进程回收
创建进程对象
Process()
功能:创建进程对象
参数:target:要绑定的函数
name:给进程起的名称(默认Process-1)
args:元组 用来给target函数位置传参
kwargs:字典 用来给target函数键值传参
p.start()
功能:启动进程 自动运行target绑定的函数。此时进程被创建
p.join([timeout])
功能:阻塞等待子进程退出
参数:超时检测
* 使用multiprocessing 创建进程子进程同样复制父进程的全部内存空间,之后有自己独立空间,执行上互不干扰
* 子进程也是有自己特有的PID等资源
* 如果不使用join回收可能会产生僵尸进程
* 使用multiprocesing创建子进程,一般父进程功能就是创建子进程回收子进程,所有事件交给子进程完成
作业:
1.梳理聊天室代码
2.对进程概念和创建过程进行巩固
3.加深对http协议的理解
4.编程:使用父子进程复制一个文件,分别复制文件的上半部分和下半部分(以字节数区分)到新的文件中
day06
Process(target, name, args, kwargs)
name:给进程起名字
args:给进程元组传参
kwargs:给进程字典传参
p.pid : 创建的新的进程的PID号
p.is_alive() : 判断进程是否alive状态
p.daemon : 如果设置为True,则主进程退出时子进程也会结束
* daemon 属性设置要在start()前
* 设置daemon为True则一般不需要加join
os.path.getsize('./xxx') 获取文件大小
如果多个子进程拷贝同一个父进程中的对象内容,则多个子进程使用的是同一个对象,如文件,套接字,队列,管道。
如果是在创建子进程后单独创建的对象,则多个子进程各部不同
进程的缺点
进程在创建和销毁的过程中消耗的资源相对较多
进程池技术
产生原因:如果有大量的任务需要多进程完成,而任务周期又比较短且需要频繁的创建。此时可能产生大量进程频繁创建销毁的情况,消耗计算机资源较大
使用方法:
创建进程池,在池内放入适当的数量的进程
将事件封装函数,放入到进程池
事件不断运行,直到所有放入进程池事件运行完成
关闭进程池,回收进程
from multiprocessing import Pool
Pool(processes)
功能:创建进程池对象
参数:进程数量
返回:进程池对象
p.apply_async(func, args, kwds) -- 异步执行
功能:将事件放入进程池执行
参数:fun 要执行的事件函数
args 以元组为fun传参
kwds 以字典为fun传参
返回值:返回事件对象的内存地址,通过get()属性函数可获取func的返回值
p.apply(func, args, kwds) -- 同步执行
功能:将事件放入进程池执行
参数:fun 要执行的事件函数
args 以元组为fun传参
kwds 以字典为fun传参
p.close()
功能:关闭进程池,无法再加入事件
p.join()
功能:回收进程池
p.map(func, iter)
功能:将要执行的事件放入到进程池
参数:func 要执行的函数
iter 迭代对象,为func传参
返回值:事件函数的返回值列表
进程间通信(IPC)
由于进程间空间独立,资源无法共享,此时在进程间通信就需要专门的通信方法
进程间通信方法:管道,消息队列,共享内存,信号,信号量,套接字
管道通信 Pipe
在内存中开辟一块空间,形成管道结构,多个进程使用同一个管道,即可通过对管道的读写操作进行通信
multiprocessing --> Pipe
fd1,fd2 = Pipe(duplex=True)
功能:创建管道
参数:默认表示双向管道
如果设置为False则为单向管道
返回值:两个管道对象,分别表示管道的两端
如果是双向管道,均可读写
如果是单向管道,则fd1只读,fd2只写
fd.recv()
功能:从管道读取信息
返回值:读取到的内容
* 当管道为空则阻塞
fd.send(data)
功能:向管道写入内容
参数:要写入的内容
* 当管道满时则阻塞
* 可以写入几乎所有的python数据
消息队列
先进先出
在内存中开辟队列结构空间,对多个进程进程课件。多个进程操作同一个队列对象可以实现消息的存取工作
创建队列
q = Queue(maxsize=0)
功能:创建队列对象
参数:maxsize 默认表示系统自动分配队列空间
如果传入正整数则表示最多存入的消息
返回值:队列对象
q.put(data[,block,timeout])
功能:向队列中存入消息
参数:data 存入的消息 (支持python数据类型)
block 默认为True 表示当队列满时阻塞
设置为False 则为非阻塞
timeout 超时时间
data = q.get([block,timeout])
功能:获取队列消息
参数:block 默认为True 表示队列空时阻塞
设置为False 表示非阻塞
timeout 表示超时时间
返回值:返回获取到的消息
q.full() 判断队列是否为满
q.empty() 判断队列是否为空
q.qsize() 获取队列中的消息数量
q.close() 关闭队列
共享内存
在内存中开辟一段孔家,存储数据,对多个进程课件。每次写入共享内存的数据会覆盖之间的内容
from multiprocessing import Value,Array
shm = Value(ctype,obj)
功能:开辟共享内存空间
参数:ctype 字符串 要转变的c类型
obj 共享内存的初始化数据 (单一数据)
返回值:返回共享内存对象
shm.value 表示共享内存中的值
shm = Array(ctype,obj)
功能:开辟共享内存空间
参数:ctype 字符串 要转变的c类型
obj 要存入共享内存的数据
* 列表、字符串 表示要存入的内容:要求类型相同
* 整数,表示要开辟几个单元空间
返回值:返回共享内存对象 可迭代对象
管道 消息队列 共享内存
开辟空间 内存 内存 内存
读写方式 两端读写 先进先出 每次覆盖上次内容
双向/单向
效率 一般 一般 较高
应用 多用于父子进程 应用灵活广泛 复杂,需要同步互斥操作
作业:熟练进程间通信和进程池的使用
复习类的使用
day07
p.name
p.pid
p.is_alive() 查看进程状态
p.daemon
自定义进程类
继承Process 重写run
进程池
Pool() 创建进程池
apply_async() 添加事件
进程间通信
管道 --- Pipe()
消息队列 ---- Queue() ----- q.put q.get q.full
共享内存 ----- Value Array
************************************************
信号
一个进程向另外一个进程通过信号的方式传递某种讯息,接收方在接收到信息后作出相应的处理
kill -l 查看信号
kill -sig PID 向一个进程发送信号
关于信号
信号名称:系统定义,名字或者数字
信号含义:系统定义,信号的作用
默认处理方法:当一个进程接收到信号时,默认产生的效果---终止进程,暂停进程,忽略发生
SIGNUP --- 断开连接
SIGINT --- Ctrl+C --- 终止
SIGQUIT --- Ctrl+\ --- 终止
SIGKILL --- 终止进程(没有非默认方法)
SIGALRM --- 时钟信号 ----终止
SIGSTOP ---- 暂停进程(没有非默认方法)
SIGTSTP --- Ctrl+Z ---暂停进程
SIGCHLD --- 子进程改变状态是,父进程会收到--忽略
python信号处理
os.kill(pid,sig)
功能:发送一个信号给某个进程
参数:pid 给哪个进程发送信号
sig 发送什么信号
signal.alarm(sec)
功能:设置时钟信号,在一定时间后给自身发送SIGALRM信号
参数:sec 时间(秒)
* 一个进程中只能有一个时钟,后来的会覆盖前面的时钟
同步:程序按照步骤一步一步执行,呈现一个先后性和顺序性
异步:程序在执行中利用内核功能帮助完成必要的辅助操作,不影响应用层持续执行
signal.pause()
功能:阻塞进程,等待一个信号
signal.signal(sig,handler)
功能:处理信号
参数:sig 要处理的信号
handler 信号处理方法
可选值:SIG_DFL 表示使用默认方法处理
SIG_IGN 表示忽略这个信号
func 自定义函数处理
自定义函数格式:
def func(sig,frame)
sig:接收到的对象
frame:信号结构对象
* signal函数也是一个异步处理函数,只要执行了该函数,则进程任意时候接收到相应信号都会处理
* signal是不能处理SIGKILL SIGSTOP的
* 父进程中可以用singnal(SIGCHLD,SIG_IGN)将子进程的退出交给系统处理
* 信号是一种异步的进程间通信方式
信号量
给定一定数量,对多个进程可见,并且多个进程根据信号量的多少确定不同的行为
sem = Semaphore(num)
功能:创建信号量对象
参数:信号量的初始值
返回:信号量对象
sem.acquire() 将信号量数量减1 当数量为0时阻塞
sem.release() 将信号量数量加1
sem.get_value() 获取当前信号量的值
同步互斥机制
目的:解决对共有资源产生的资源争夺
临界资源:多个进程或线程都能操作的资源
临界区:操作临界资源的代码段
同步:同步是一种合作关系,为完成某个任务,多进程或多线程之间形成一种协调关系,安装约定执行,相互告知,共同完成任务
互斥:互斥是一种制约关系,当一个进程或者线程进入临界区会进行枷锁操作。此时其他进程(线程)在企图使用临界资源时就会阻塞,知道资源被释放才能使用
Event事件
from multiprocessing import Event
创建时间对象
e = Event()
事件阻塞
e.wait([timeout])
功能:使进程处于闭塞状态,直到事件对象呗set
事件设置 e处于设置状态,e.wait()不在阻塞
e.set()
事件清除 e处于未设置状态,e.wait会阻塞
e.clear()
事件判断,判断e的当前状态
e.is_set()
锁 Lock
multiprocessing ---> Lock
创建锁对象
lock = Lock()
lock.acquire() 上锁
lock.release() 解锁
* 如果一个锁对象已经被上锁则在调用acquire会阻塞
with lock: 上锁
....
....
解锁
多线程 thread
线程也是一种多任务编程方式,可以使用计算机的多核资源
线程被称为轻量级进程
线程特征:
一个进程可以包含多个线程
线程计算机内核使用的最小单位
线程也是一个运行过程,也要消耗计算机资源
多线程共享共用进程的资源
线程也有自己的特征属性,TID,指令集,线程栈
多个线程之间独立运行互不干扰
线程创建删除消耗的资源要小于进程
threading 模块使用
threading.Thread()
功能:创建线程对象
参数:target 线程函数
name 线程名称 默认为thread-1,thread-2...
args 元组 给线程函数位置传参
kwargs 字典 给线程函数字典传参
返回:线程对象
t.start() 启动线程
t.join([timeout]) 回收线程
t.is_alive()
t.name
t.setNname()
默认情况下主线程的结束不会影响到分支线程的执行
如果设置为True 则主线程退出分支线程也会退出
设置方法
t.daemon = True
t.setDaemon(True)
判断daemon属性
t.isDaemon()
* 线程daemon属性的设置在start前
* 一般设置daemon后不会再使用join
创建自己的线程类
步骤:
1.继承 Thread类
2.运行 Thread类中的__init__方法以获取父类原有属性
3.重写run方法
day08
信号:在进程间通过信号传递信息
os.kill(pid,sig)
signal.alarm(sec)
signal.pause()
signal.signal(sig,handler)
信号量 semaphore
acquire()
release()
get_value()
tftp 文件服务器
1.文件处理
2.类的封装
3.网络
线程的同步互斥
线程Event
创建对象
e = Treading.Event()
事件阻塞函数
e.wait([timeput])
设置事件
e.set()
清除事件
e.clear()
python 线程的GIL问题
GIL(全局解释器锁)
python ---> 支持线程操作 ---> IO同步互斥问题 ---> 加锁 ---> 超级锁,给解释器加锁
后果:一个解释器,同一时刻只能解释一个线程,此时其他线程需要等待。大大降低了python线程的执行效率
python GIL问题解决方案
* 修改c解释器
* 尽量使用多进程进行并行操作
* python线程可以用在高延迟多阻塞的io情形
* 不使用cpython --> c# java 做解释器
进程和线程的区别和联系
1.两者都是多任务编程的方式,都能够使用计算机的多核
2.进程的创建删除要比线程消耗更多的计算机资源
3.进程空间独立,数据安全性好,有专门的进程间通信方法
4.线程使用全局变量通信,更加简单,单往往要与同步互斥机制共用
5.一个进程可以包含多个线程,线程共享进程的资源空间
6.进程线程都有自己特有的资源,如命令,属性,id等
使用情况:
* 一个进程中并发任务较多,比较简单,适合使用多线程
* 如果数据程序比较复杂,特别是可能多个任务通信比较多的时候,要考虑到使用线程同步互斥的复杂性
* 多个任务存在明显差异,和功能分离的时候没有必要一定写入到一个进程中
* 使用python考虑线程GIL问题
要求:
1.进程线程的区别和关系
2.进程间的通信方式及特点
3.同步和互斥是什么回事,用什么方法实现同步互斥
4.什么是僵尸进程,怎么处理的
5.python线程的效率怎么样,gil是怎么处理的
**********************************************
服务器模型
硬件服务器:主机
厂商:IBM HP 联想 浪潮
软件服务器:编写的服务器程序,依托硬件服务器运行。提供给用户一定的功能服务。
服务器种类:
webserver ---> 网络的后端应用程序,提供数据处理和逻辑处理
httpserver ---> 接收http请求,返回http响应
邮箱服务器 ---> 处理邮件请求,进程邮件收发
文件服务器 ---> 提供文件的上传下载存储
功能实现:网络连接,逻辑处理,数据运算,数据交互,协议实现,网络数据传输.....(后端功能)
模型结构:C/S 客户端/服务器模型
B/S 浏览器/服务器模型
网络服务器基础
循环服务器:单进程服务器,循环接收客户端请求,处理请求。处理完毕再接收下一个请求
特点:每次只能处理一个客户端请求
如果客户端长期占有服务器则无法处理其他客户端请求
优点:实现简单,占用资源少
缺点:无法同时处理多个客户端,体验差
使用情况:任务短暂,可以快速完成,udp比tcp更适合循环
并发服务器:能够同时处理多个客户端任务请求
IO并发:IO多路复用 协程
优点:可以实现IO的并发操作,占用系统资源少
缺点:不能监控cpu密集的情况,不能长期阻塞消息收发
多进程/多线程并发:
为每个客户端单独提供一个进程/线程处理客户端请求
多进程并发模型
使用fork完成并发
1.创建套接字,绑定,监听
2.等待接收客户端连接请求
3.创建新的进程处理客户端请求
父进程继续等待连接其他客户端
4.客户端退出 对应子进程也要结束
tftp文件服务器
项目功能:
* 客户端有简单的页面命令提示
* 功能包含:
1.可以查看服务区文件库中的文件列表
2.可以下载其中的某个文件到本地
3.可以上传客户端文件到服务器文件库
* 服务器需求:
1.允许多个客户端同时操作
2.每个客户端可能会连续发送命令
技术分析:
1.tcp套接字跟适合文件传输
2.并发方案 ---> fork 多进程并发
3.对文件的读写操作
4.获取文件列表 ---> os.listdir()
粘包处理
整体结构设计
1.服务器功能分装在类中(上传,下载,查看列表)
2.创建套接字,流程函数调用 main()
3.客户端负责发起请求,接收回复,展示
服务端负责接收请求,逻辑处理
编程实现
1.搭建整体结构,创建网络连接
2.创建多进程和类的结构
3.每个功能模块的实现
day09
回顾
线程
线程通信 ---> 全局变量
线程的同步互斥 ---> Event Lock
python GIL 问题
服务器模型
循环模型
每次只能处理一个客户端请求
并发模型
IO并发:IO多路复用 协程
多进程、多线程并发
每当有一个客户端就创建一个新进程
多进程并发
tcp + fork
******************************************
文件服务器
cookie
os.listdir(path)
功能:获取文件列表
os.path.isfile(obj) 判断一个文件是否为普通文件
os.path.isdir(obj) 判断一个文件是否为目录
多线程并发
threading 模块完成多线程并发
对比多进程并发
优势:资源消耗少
缺点:需要注意对共享资源的操作影响
实现步骤:
1.创建套接字,绑定,监听
2.接受客户端连接技术,创建新的进程
3.主线程继续等待其他客户端连接,分支线程执行客户端具体请求
4.处理完客户端请求后分支线程自然退出,关闭客户端套接字
socket 并发集成模块
python2 --> SocketServer
python3 --> socketserver
功能:通过模块提供的接口组合可以完成多进程/多线程 tcp/udp的并发程序
'StreamRequestHandler', 处理tcp请求
'DatagramRequestHandler', 处理udp请求
'ForkingMixIn', 创建多进程
'ThreadingMixIn', 创建多线程
'TCPServer', 创建tcp server
'UDPServer', 创建udp server
'ForkingTCPServer', TCPServer+ForkingMixIn
'ForkingUDPServer', UDPServer+ForkingMixIn
'ThreadingTCPServer', TCPServer+ThreadingMixIn
'ThreadingUDPServer', UDPServer+ThreadingMixIn
基于多线程并发的 HTTPServer
1.接收浏览器http请求
2.对请求进行一定解析
3.根据解析结果返回对应的内容
4.如果没有请求内容则返回404
5.组织Response格式进行回发
升级:
* 使用多线程并发
* 增加了具体请求解析和404
* 使用类进行封装
* 增加了一定的数据获取能力