Python Day25 进程与网络编程

一、进程的创建方式

1. os.fork () 方法

  • 功能:创建一个子进程。
  • 适用范围:只能应用在 Unix 类型的操作系统中,Windows 操作系统不支持该方式。
  • 返回结果:该方法会返回 2 次结果
    • 如果返回 0,代表当前处于子进程环境。
    • 如果返回一个大于 0 的数字,该数字为子进程的 PID。

2. 使用 multiprocessing 模块中的 Process 类(面向过程)

通过 target 指定函数的方式创建进程,示例代码如下:

复制代码
from multiprocessing import Process
import time

def excute_task(array):
    array[0] = 100
    array[-1][0] = 600
    print('我是一个子进程', array)

if __name__ == '__main__':
    array = [1, 2, 3, 4, 5, [6, 7]]
    process = Process(target=excute_task, args=(array,), name='测试进程')
    process.start()
    print(array)
    time.sleep(0.11)
    print('主进程结束')

3. 继承 multiprocessing 中的 Process 类并重写 run 方法

(注:此处原笔记未提供示例代码,可根据面向对象思想,继承 Process 类后重写 run 方法实现进程功能)

4. 使用进程池构建

(注:此处原笔记未提供示例代码,进程池可通过 multiprocessing 模块的 Pool 类实现,方便管理多个进程)

进程数据特性

子进程会将主进程中的数据进行深克隆,每个进程间数据相互独立。

二、多进程实现售票系统

复制代码
from multiprocessing import Process, current_process, Queue, Lock
import random
import time

class Ticket:
    def __init__(self, number):
        self.number = f"NO.{number:0>6}"

    def __repr__(self):
        return self.number

def execute_task(queue, lock):
    while not queue.empty():
        time.sleep(random.uniform(0.2, 0.5))
        with lock:
            if queue.qsize() > 0:
                ticket = queue.get()
                print(f"{current_process().name}正在售票、票号是{ticket}、剩余{queue.qsize()}")
            else:
                print(f"{current_process().name}发觉票已售罄!!!")

if __name__ == '__main__':
    queue = Queue(maxsize=100)
    for x in range(100, 200):
        queue.put(Ticket(x))
    lock = Lock()
    # 开启多个进程负责售票
    process = Process(target=execute_task, args=(queue, lock), name=f'进程-1')
    process.start()

    process = Process(target=execute_task, args=(queue, lock), name=f'进程-2')
    process.start()

    process = Process(target=execute_task, args=(queue, lock), name=f'进程-3')
    process.start()

三、死锁

死锁的定义

死锁是指在多线程或多进程编程中,多个线程或进程因争夺资源而相互等待,导致它们永远无法继续执行下去的情况。

死锁的四个必要条件

  • 互斥条件:至少有一个资源必须处于 "非共享" 模式,即某个资源一次只能被一个进程使用。
  • 占有并等待:至少有一个进程持有一个资源,并等待其他进程持有的资源。
  • 非抢占条件:已分配给一个进程的资源,在该进程使用完之前,不能强行剥夺。
  • 循环等待条件:存在一种进程资源的循环等待关系,即进程 A 等待 B 持有的资源,B 等待 C 持有的资源,C 等待 A 持有的资源,形成一个闭环。

死锁的解决方案

死锁预防试图通过打破死锁的四个必要条件中的至少一个来避免死锁:

  • 打破互斥条件:将某些资源设置为共享资源。适用于只读资源。
  • 打破占有并等待条件:进程在请求资源时,必须先释放已持有的所有资源。例如,进程 A 请求 R2 之前,必须释放 R1。
  • 打破非抢占条件:如果进程已经持有一些资源并且无法继续执行(比如它等待资源的其他进程),系统可以强制回收该进程持有的资源,并分配给其他进程。
  • 打破循环等待条件:资源请求的顺序应当固定,保证资源的请求顺序不形成环。例如,要求进程按顺序申请资源:R1、R2、R3。

四、OSI 七层模型

  • 应用层
  • 表示层
  • 会话层
  • 传输层:主要包含 TCP 和 UDP 数据传输协议
  • 网络层
  • 数据链路层
  • 物理层

五、协议相关

协议的定义

协议是一种数据传输规则。

TCP 协议

  • 是一种面向连接的、安全的数据传输协议。
  • 通过三次握手建立链接,四次挥手断开链接。
  • 保证数据在网络中传输的稳定性,但数据传输速度较慢。
  • 网站主要采用的是 TCP 协议,在应用层主要表现为 http 和 https 协议。

UDP 协议

  • 是一种无连接的数据传输协议。
  • 通过报文的形式进行数据传输。
  • 数据传输是不安全的,可能存在丢失数据的风险。
  • 数据传输速度非常快。
  • 邮件、视频会议主要采用 UDP 协议。

六、socket 套接字

基本概念

  • TCP 协议是一个面向连接的稳定的数据传输协议,主要通过二进制流的形式进行数据传输。
  • socket(family = -1, type = -1)
    • family:设置数据的传输方式,默认是 socket.AF_INET,采用 IPV4 方式。
    • type:设置传输的类型,默认是 socket.SOCK_STREAM(基于 TCP 协议,基于二进制流),支持 SOCK_DGRAM(UDP 协议)。
  • 端口号取值范围是 0~65535。
  • IP 地址可以用来标记一台计算机,使用端口号进行服务的区分,例如:
    • MySQL 默认端口 3306
    • Oracle 默认端口 1521
    • Redis 数据库默认端口号是 6379

TCP 服务器示例代码

复制代码
import socket

# 1.创建一个socket server对象
socketserver = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 2.绑定服务器的IP和端口
# 可以在命令提示符下使用ipconfig查看当前计算机的IP地址
# 作为服务器也可以绑定0.0.0.0、允许任意计算机访问
socketserver.bind(("192.168.11.134", 9999))

# 3.监听客户端连接的数量
socketserver.listen(5)

while True:
    print("服务器正在等待客户端链接...")
    # 4.被动等待客户端的链接、accept会阻塞程序的运行、直到某个客户端连接
    # 并返回客户端和服务器的连接通道
    client_socket, addr = socketserver.accept()
    while True:
        # 5.服务器获取客户端发送的消息
        data = client_socket.recv(8 << 10)
        print(f"客户端{addr}发送的信息是:")
        # 输出客户端发送的内容
        print(data.decode())

        # 6.服务器向客户端发送消息
        send_data = input("输入你要发送的消息")
        client_socket.send(send_data.encode())

        # 7.断开和客户端的链接(此处原代码未实现具体断开逻辑)

TCP 客户端示例代码

复制代码
import socket

# 创建一个基于TCP协议的套接字对象
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 链接TCP服务器
client.connect(("192.168.11.149", 9999))

# 3.向服务器发送消息
while True:
    data = input("请输入你要发送的消息:")
    client.send(data.encode())

    # 4.接受服务器发过来的消息
    msg = client.recv(8 << 10)
    print('服务器发送的消息:')
    print(msg.decode())

七、聊天室 - 服务器

复制代码
import socket
import threading
from datetime import datetime
import json

class User:
    """表示用户"""
    def __init__(self, id, name):
        self.id = id
        self.name = name

    def __repr__(self):
        return f"{self.__class__.__name__}({self.id},{self.name})"

class Message:
    def __init__(self, user, message):
        self.user = user
        self.message = message
        self.creat_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

"""
如果是身份信息 {'type':'register','name':'张三'}
如果是消息 {'type':'message','from':'张三','text':"ffffff",'creat_time':'2025-5-1'}'}
"""

def execute_massage(client: socket.socket, socket_dict: dict, lock):
    while True:
        # 不断地接收和转发消息
        data = client.recv(8 << 10).decode()
        # 将data进行反序列化
        dct = json.loads(data)
        # 如果反序列化成功、说明传入的是正常的聊天信息
        # 遍历字典、给除自己之外的所有人发送消息
        if dct["type"] == "register":
            with lock:
                socket_dict[dct["name"]] = client
            print(f'{dct["name"]}用户上线了')
            client.send("欢迎进入聊天室".encode())
        else:
            for sc in socket_dict.values():
                if sc != client:
                    sc.send(data.encode())

if __name__ == '__main__':
    # 创建一个socket套接字对象
    socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # 绑定主机和端口号
    socket_server.bind(('0.0.0.0', 8080))

    # 监听客户端的连接数量
    socket_server.listen(5)

    # 定义一个字典、给所有线程共享
    socket_dict = {}

    while True:
        # 模拟不断地接收客户端的请求
        client, addr = socket_server.accept()

        # 定义一个锁对象
        lock = threading.Lock()

        # 给每一个客户端开启一个线程
        threading.Thread(target=execute_massage, args=(client, socket_dict, lock)).start()

八 、 聊天室 - TCP 客户端实现

以下是聊天室的 TCP 客户端实现代码,用于连接聊天室服务器并进行消息发送和接收:

复制代码
import socket
import json
import threading
from datetime import datetime


class TcpClient:
    def __init__(self, name):
        self.name = name
        # 创建TCP套接字并连接服务器
        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        client.connect(("192.168.11.132", 8888))
        self.client = client
        self.lock = threading.Lock()
        
        # 向服务器注册用户信息
        dct = {'type': 'register', 'name': name}
        client.sendall(json.dumps(dct).encode())
        # 接收服务器的欢迎信息
        data = client.recv(8 << 10)
        print(data.decode())

    def chat(self):
        # 启动消息读取和发送线程
        threading.Thread(target=self.read_message, args=()).start()
        threading.Thread(target=self.send_message, args=()).start()

    def read_message(self):
        # 持续接收并显示其他用户发送的消息
        while True:
            message = self.client.recv(8 << 10)
            # 对接收的数据进行反序列化
            dct = json.loads(message)
            with self.lock:
                print(f"{dct['name']}\t{dct['create_time']}")
                print(f"📄{dct['text']}")

    def send_message(self):
        # 持续读取用户输入并发送消息
        while True:
            message = input()
            message_dict = {
                'type': 'message',
                'name': self.name,
                'text': message,
                'create_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            }
            self.client.sendall(json.dumps(message_dict).encode())


if __name__ == '__main__':
    # 创建一个客户端对象并启动聊天功能
    user1 = TcpClient('大牛头')
    user1.chat()

代码说明

  1. TcpClient 类:封装了 TCP 客户端的所有功能

    • __init__方法:初始化客户端,创建套接字并连接服务器,完成用户注册
    • chat方法:启动消息读取和发送的线程
    • read_message方法:持续接收服务器转发的其他用户消息并显示
    • send_message方法:读取用户输入并发送消息到服务器
  2. 消息格式

    • 注册消息:{'type': 'register', 'name': 用户名}
    • 聊天消息:{'type': 'message', 'name': 用户名, 'text': 消息内容, 'create_time': 发送时间}
  3. 多线程处理

    • 一个线程负责读取并显示消息
    • 一个线程负责获取用户输入并发送消息
    • 使用锁 (lock) 保证消息显示的完整性
  4. 运行方式:实例化 TcpClient 类并调用 chat () 方法即可进入聊天状态

相关推荐
凌晨7点几秒前
控制建模matlab练习13:线性状态反馈控制器-②系统的能控性
开发语言·matlab
武文斌7710 分钟前
数据结构:哈希表、排序和查找
数据结构·散列表
要记得喝水14 分钟前
汇编中常用寄存器介绍
开发语言·汇编·windows·c#·.net
金智维科技官方19 分钟前
常见的大模型分类
人工智能·算法·ai·语言模型·数据挖掘
yzzzzzzzzzzzzzzzzz29 分钟前
leetcode热题——有效的括号
算法·
shi578329 分钟前
C# 常用的线程同步方式
开发语言·后端·c#
凌晨7点37 分钟前
控制建模matlab练习11:伯德图
开发语言·matlab
码农派大星。1 小时前
Selenium在Pyhton应用
python·selenium·测试工具
崎岖Qiu1 小时前
leetcode1343:大小为K的子数组(定长滑动窗口)
java·算法·leetcode·力扣·滑动窗口
freed_Day1 小时前
Java学习进阶--集合体系结构
java·开发语言·学习