1 进程与线程
1.1 多进程基础
- 并发:一段时间内
交替执行
多个任务(任务数量大于CPU核心数) - **并行 **:一段时间内
同时一起执行
多个任务(任务数量小于或等于CPU核心数) - 进程:资源分配的最小单位,操作系统进行
资源分配和调度运行的基本单位
,一个正在运行的程序就是一个进程
进程的创建步骤
1 导入进程包:import multiprocessing
2 通过进程类创建进程对象:进程对象 = multiprocessing.Process(target=任务名)
3 启动进程执行任务:进程对象.start()
import multiprocessing
import time
def coding():
for i in range(3):
print('coding...')
time.sleep(0.2)
def music():
for i in range(3):
prin('music...')
time.sleep(0.2)
if __name__ == '__mian___':
# 通过进程类创建对象
coding_process = multiprocessing.Process(target=coding)
music_process = multiprocessing.Process(target=music)
# 启动进程
coding_process.start()
music_process.start()
进程执行带参数的任务
参数名 | 说明 |
---|---|
args | 以元祖的方式执行任务传参 |
kwargs | 以字典的方式执行任务传参 |
进程执行带有参数的任务传参有两种方式:
1 元组方式传参 :元组方式传参一定要和参数的顺序保持一致
2 字典方式传参:字典方式传参字典中的key一定要和参数名保持一致
# 通过进程类创建对象(带参数)
coding_process = multiprocessing.Process(target=coding,args=(3,))
music_process = multiprocessing.Process(target=music,kwargs={"count",2})
# 启动进程
coding_process.start()
music_process.start()
获取进程编号
1 获取当前进程编号:getpid( )方法
2 获取当前父进程编号:getppid( )方法
import os
def work():
print('work进程编号:',os.getpid())
print('work父进程编号:',os.getppid())
注意:进程之间不共享全局变量
import multiprocessing
import time
my_lst = [] # 创建全局变量
def writedata():
for i in range(3):
my_list.append(i)
print('write_date:',my_list)
def read_data():
print('read_data:',my_list)
if __name__ == '__main__':
# 创建进程
write_processs = multiprocessing.Procss(target=write_data)
read_process = multiprocessing.Process(target= read_data)
# 启动进程
write_process.start()
time.sleep(1)
read_process.start()
主进程与子进程的结束顺序
1 主进程会等待所有的子进程执行结束后再结束
2 设置守护主进程:主进程退出后子进程直接销毁,不在执行子进程中的代码,子进程对象.daemon = True
3 销毁子进程:主进程退出之前把所有的子进程直接销毁,子进程对象.terminate()
import multiprocessing
import time
def work():
for i in range(10):
print('子进程工作中:')
if __name__ == '__main__':
# 创建进程
work_processs = multiprocessing.Procss(target=work)
work_process.daemon = True # 方法1:设置守护主进程
wrork_process.start()
time.sleep(1)
work_process.termnate() # 方法2:销毁子进程,主进程退出之前把所有的子进程直接销毁
print('主进程执行完了!')
1.2 多线程基础
线程:程序执行的最小单位,一个进程中最少有一个线程来负责执行程序,同一个进程中的多个线程共享进程所拥有的全部资源
线程的创建步骤
1 导入线程模块:import thrading
2 通过线程类创建线程对象:线程对象 = threading.Thread(target=任务名)
3 启动进程执行任务:进程对象.start()
import threading
import time
def coding():
for i in range(3):
print('coding...')
time.sleep(0.2)
def music():
for i in range(3):
prin('music...')
time.sleep(0.2)
if __name__ == '__mian___':
# 通过线程类创建对象
coding_thread = threading.Thread(target=coding)
music_thread = threading.Thread(target=music)
# 启动线程
coding_thread.start()
music_thread.start()
注意:
1 线程执行带参数的任务,与进程相同
2 主线程会等待所有的子线程执行结束后主线程再结束,
- 设置守护主线程:
threading.Thread(target=work,daemon=True)
- 销毁子线程:
子线程对象.setDaemon(Ture)
if __name__ == '__main__':
# 创建线程
# 方法1:设置守护主线程
work_thread = threading.Thread(target=work,daemn=Ture)
# 启动线程
work_thrad.setDeamon(Ture) # 方法2:销毁子线程
wrork_streads.start()
time.sleep(1)
print('主线程执行完了!')
3 线程之间执行是无序
的
- 通过current_thread方法获取线程对象:
current _thread = threading.current_thread()
- 通过current_thread对象可以知道线程的相关信息(如线程创建的顺序):
print(current_thread)
4 多个线程在同一个进程中,线程之间共享全局变量
5 多线程同时操作全局变量时,会导致数据出现错误,可以用线程同步(互斥锁)
方式解决问题
互斥锁:对共享数据进行锁定,保证同一时刻只有一个线程去操作,多个线程同时抢互斥锁,没有抢到的线程需等待到其他线程释放之后再去抢这个锁
互斥锁的使用:
1 互斥锁的创建:mutex = threading.Lock()
2 上锁:mutex.acquire()
3 释放锁:mutex.release()
import threading
g_num = 0
def sum_num1():
mutex.acquire() # 上锁
for i in range(10000):
global g_num
g_num += 1
mutex.release() # 解锁
print('g_num1:',g_num)
def sum_num2():
mutex.acquire() # 上锁
for i in range(10000):
global g_num
g_num += 1
mutex.release() # 解锁
print('g_num2:',g_num)
if __name__ = "__main__":
# 创建互斥锁
mutex = threading.Lock()
sum1_thread = threading.Thread(target=sum_num1)
sum2_thread = threading.Thread(target=sum_num2)
sum1_thread.start()
sum2_thread.start()
死锁:一直等待对方释放锁的情景就是死锁
进程与线程的对比
1 线程是依附在进程里面的,没有进程就没有线程
2 一个进程默认提供一条线程,进程可以创建多个线程
3 进程之间不能共享全局变量,但是线程之间可以
4 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位
5 进程可以使用多核,但是资源开销大,线程相反
2 网络编程
2.1 网络编程基础
网络:实现资源共享和信息传递的虚拟平台
socket:程序键网络数据通信的工具
IP地址:网络设备上网使用的数字标签,能够标识网络中唯一的一台设备
IP地址分为IPv4和IPv6两类,IPv4是目前使用的ip地址,IPv6是未来使用的ip地址
命令名 | 说明 |
---|---|
ifconfig | 查看网卡信息 |
ping | 检查网络是否正常 |
端口:传输数据的通道,每一个端口都有一个对应的端口号,端口号有65536个
1 知名端口号:众所周知的端口号(0-1023),21端口号分配给FTP文件传输协议服务。25端口号分配给SMTP简单邮件传输协议服务,80端口号分配给HTTP服务
2 动态端口号:程序员开发应用程序使用的端口号(1024-65535)
3 运行一个程序默认会有一个端口号,退出程序时所占用的端口号会被释放
2.2 TCP介绍
TCP:传输控制协议,一种面向连接的、可靠的、基于字节流的传输通信协议
- 面向连接:通信双方必须先建立好连接才能进行数据的传输,并且双方都会为此连接分配必要资源用来记录连接的状态和信息
- 可靠传输:TCP采用发送应答机制、超时重传、错误校验、流量控制和阻塞管理
数据的编码转化
- encode:编码,将字符串转化为字节码,str.encode(encoding=“utf-8”)
- decode:编码,将字节码转化为字符串,bytes.encode(encoding=“utf-8”)
TCP客户端程序开发
基本步骤:创建客户端套接字对象(买电话)——和服务端套接字建立连接(打电话)——发送数据(说话)——接收数据(接听)——关闭客户端套接字(挂电话)
客户端程序开发步骤:
- 导入socket模块(
import socket
)——创建客户端socket对象是用socket类(socket.socket(AddressFamily,Type)
) AddressFamily
指代的是IP地址类型,Type
指代的是传输协议类型connect
(和服务端套接字建立连接)、send
(发送数据)、recv
(接收数据)、close
(关闭连接)
import socket
if __name__ == '__main__':
# 1 创建客户端套接字对象
tcp_client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 2 和服务端套接字建立连接
tcp_client_socket.connect(("192.168.81.283",8080)) # 此处输入要连接的ip地址
# 3 发送数据
tcp_client_socket.send("nihao".encode(encoding="utf-8"))
# 4 接收数 recv 阻塞等待数据的到来
rec_data = tcp_client_socket.recv(1024)
print(recv_data.decode())
# 5 关闭客户端套接字
tcp_client_socket.close()
TCP服务端程序开发
基本步骤:创建服务端端套接字对象——绑定IP地址和端口号——设置监听——等待接受客户端的连接请求
——接收数据——发送数据——关闭套接字
服务端程序开发步骤:
- 导入socket模块(
import socket
)——创建客户端socket对象是用socket类(socket.socket(AddressFamily,Type)
) AddressFamily
指代的是IP地址类型,Type
指代的是传输协议类型bind
(绑定IP地址和端口号)、listen
(设置监听)、accept
(等待客户的连接请求)、recv
(接收数据)、close
(关闭连接)
import socket
if __name__ == '__main__':
# 1 创建服务端套接字对象
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 设置端口复用,放置误认为端口号占用
tcp_sercer_socket = socket.socket(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
# 2 绑定IP地址和端口号
# tcp_server_socket.bind(("192.168.81.283",8888))
tcp_server_socket.bind(("",8888)) # 第一个元素为空,默认本机IP地址
# 3 设置监听 128:代表服务端等待排队链接的最大数量
tcp_server_socket.listen(128) # 第一个元素为空,默认本机IP地址
# 4 等待客户端的连接请求 accept阻塞等待 返回一个用以和客户端通信的socket,客户端的地址
conn_socket,ip_port = tcp_server_socket.accept()
print('客户端地址:',ip_port)
# 5 接收数 recv 阻塞等待数据的到来
rec_data = conn_socket.recv(1024)
print('接受的数据:',recv_data.decode())
# 6 发送数据
conn_socket.send("客户端,你的数据我收到了".encode(encoding="utf-8"))
# 7 关闭套接字
conn_socket.close()
tcp_server_socket.close()
注意:
1 send:发数据必须得通过网卡发送数据,应用程序是无法直接通过网卡发送数据的,它需要调用操作系统接口,也就是说,应用程序把发送的数据先写入到发送缓冲区(内存中的一片空间),再由操作系统控制网卡把发送缓冲区的数据发送给服务端网卡
2 recv:应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区(内存中的一片空间),应用程序再从接收缓存区获取客户端发送的数据
3 服务端服务多个客户端:listen之后加上while true循环,但是注意不要关闭服务端tcp_server_socket
while True:
# 4 等待客户端的连接请求 accept阻塞等待 返回一个用以和客户端通信的socket,客户端的地址
conn_socket,ip_port = tcp_server_socket.accept()
print('客户端地址:',ip_port)
# 5 接收数 recv 阻塞等待数据的到来
rec_data = conn_socket.recv(1024)
print('接受的数据:',recv_data.decode())
# 6 发送数据
conn_socket.send("客户端,你的数据我收到了".encode(encoding="utf-8"))
# 7 关闭套接字
conn_socket.close()
tcp_server_socket.close()
4 服务端同时服务多个客户端:当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞
import socket
import threading
# 处理客户端函数
def handle_client(conn_socket):
# 5 接收数 recv 阻塞等待数据的到来
rec_data = conn_socket.recv(1024)
print('接受的数据:',recv_data.decode())
# 6 发送数据
conn_socket.send("客户端,你的数据我收到了".encode(encoding="utf-8"))
# 7 关闭套接字
conn_socket.close()
if __name__ == '__main__':
# 1 创建服务端套接字对象
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 设置端口复用,放置误认为端口号占用
tcp_sercer_socket = socket.socket(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
# 2 绑定IP地址和端口号
# tcp_server_socket.bind(("192.168.81.283",8888))
tcp_server_socket.bind(("",8888)) # 第一个元素为空,默认本机IP地址
# 3 设置监听 128:代表服务端等待排队链接的最大数量
tcp_server_socket.listen(128) # 第一个元素为空,默认本机IP地址
while True:
# 4 等待客户端的连接请求 accept阻塞等待 返回一个用以和客户端通信的socket,客户端的地址
conn_socket,ip_port = tcp_server_socket.accept()
print('客户端地址:',ip_port)
# 使用多线程去接收多个客户的请求
sub_thread = threding.Thread(target=handle_client,args=(conn_socket),) # 注意需要将参数传入处理客户端函数
sub_thread.start()
tcp_server_socket.close()
2.3 HTTP协议
网址:URL,统一资源定位符(协议+域名+资源路径+可选参数)
域名:IP地址的别名,它是用点进行分割使用英文字母和数字组成的名字,使用域名目的就是方便的记住某台主机IP地址
参数说明:?后面page表示第一个参数,后面的参数都是用 & 进行连接
http协议:超文本传输协议,规定浏览器和web服务器之间通讯的数据格式
- 超文本是指在文本数据的基础上还包括非文本数据,而这些非文本数据会使用链接的方式进行加载显示(
超文本就是带有链接的文本数据
,也就是我们常说的网页数据) - 传输HTTP协议格式的数据是
基于TCP传输协议
的,发送数据之前需要先建立连接。TCP传输协议是用来保证网络中传输的数据的安全性的,HTTP协议是用来规定这些数据的具体格式的
浏览器访问web服务器的过程
HTTP请求报文(分为GET和POST两种格式)
GET
:获取Web服务器的数据,请求行+请求头+空行
POST
:向Web服务器提交数据,请求行+请求头+空行+请求体
HTTP响应报文:请求行+请求头+空行+请求体
状态码:表示Web服务器响应状态的3为数字代码
状态码 | 说明 |
---|---|
200 | 服务器已经成功处理了请求 |
400 | 错误的请求,请求地址或者参数错误 |
404 | 请求资源在服务器不存在 |
500 | 服务器内部源代码出现问题 |
HTTP协议的通讯过程