十天学会Python——第9天:多任务编程与网络编程

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协议的通讯过程
在这里插入图片描述

  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值