zoukankan      html  css  js  c++  java
  • Python网络编程(子进程的创建与处理、简单群聊工具)

    前言:
    昨天我们已经了解了多进程的原理以及它的实际使用

    Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊。普通的函数调用,调用一次,返回一次,

    但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回。

    子进程永远返回0,而父进程返回子进程的ID。这样做的理由是,一个父进程可以fork出很多子进程,所以,

    父进程要记下每个子进程的ID,而子进程只需要调用getppid()就可以拿到父进程的ID。

    Python的os模块封装了常见的系统调用,其中就包括fork,可以在Python程序中轻松创建子进程

    既然是进程那么就会有运行和退出
    接下来我们就来了解一下进程的退出以及处理
     
    孤儿进程:
        当父进程于子进程退出,此时子进程就会成为孤儿进程
        特征:
             孤儿进程会被系统指定进程收养,即系统进程成为
     这个孤儿进程新的父进程,系统进程会自动处理进程退出状态
     
    僵尸进程:
        当子进程于父进程退出父进程没处理子进程的退出状态
        此时子进程就会成为僵尸进程
        僵尸进程会滞留部分PCB信息在内存中,大量的僵尸进程会消耗系统给的内存资源
        所以要尽量避免僵尸进程的产生
     
    如何避免僵尸进程的产生:
        1.父进程先退出
        2.父进程处理子进程状态
     
        PID,status = os.wait
          功能:
             在父进程中阻塞等待处理子进程的退出
          返回值:
               pid :退出的那个子进程PID
       status :子进程的退出状态
          获取原来的退出状态:
                 wait(status)
          pid,status = os.waitpid(pid,option)
              功能:在父进程阻塞等待处理子进程的退出
              参数 :
                        pid   -1 表示等待任意子进程退出 
                                >0 表示等待对应PID号的子进程退出 option 
                                   0 表示阻塞等待 WNOHANG 表示非阻塞 
              返回值:pid 
                            退出的那个子进程的PID号 status 子进程的退出状态 
     
     
    创建二级子进程
    • 父进程创建子进程等待进程退出
    • 子进程创建下一个进程,然后立即退出
    • 二级子进程成为孤儿进程  处理具体工作
     
     
    multiprocessing  模块创建进程
      1.需要将要做的事情封装为函数
      2.使用multiprocessing提供的process 创建进程对象
      3.通过进程对象和process初始化进程进行进程的设置绑定函数
      4.启动进程,会自动执行绑定的函数
      5.完成进程的回收
     
    创建进程对象:
       process()
         功能:
           创建进程对象
         参数:
           target函数对象
           name 进程称()
           args元组 用来给target函数位置传参
           kwargs字典 用来给target函数键值传参
       p.start()
         功能:
           启动进程自动运行terget绑定函数,
           此时进程被创建
       p.join([timeout])
         功能:
           等待阻塞子进程退出
         参数:超时检测
         如果不使用join回收可能产生僵尸进程
     
    • 使用multiprocessing创建进程子进程同样复制父进程的全部内存空间
    • 之后自己的独立空间 执行互不干扰 子进程也有自己的PID特有资源等
    • 使用multiprocessing创建子进程,一般父进程功能就是创建子进程
    • 回收子进程返回事件交给子进程完成
     
     
     
     
     
     
    简单群聊:
     
    功能:
      类似QQ群聊
      1.进入聊天室需要输入姓名 姓名不能重复
      2.有人进入聊天室会向其他人发起通知  xxx进入聊天室
      3.如果一个人发消息则其他人都能收到  xxx说:...
      4.如果某个人退出聊天室也会收到通知  xxx退出聊天室
      5.服务端可以喊话:此时群里所有的都能收到服务端消息  管理员说:...
     
     
    服务器端:

    from socket import * 
    import os, sys
    
    
    # 发送管理员消息
    def do_child(s, addr):
        while True:
            msg = input("管理员消息:")
            msg = "C 管理员 " + msg
            s.sendto(msg.encode(), addr)
    
    
    # 用户登录
    def do_login(s, user, name, addr):
        if (name in user) or name == "管理员":
            s.sendto("该用户已存在".encode(), addr)
            return
        s.sendto(b'OK', addr)
        # 通知所有人
        msg = "
    欢迎 %s 进入聊天室" % name
        for i in user:
            s.sendto(msg.encode(), user[i])
        # 插入user
        user[name] = addr
    
    
    def do_chat(s, user, name, data):
        msg = "
    {} 说: {}".format(name, data)
        for i in user:
            if i != name:
                s.sendto(msg.encode(), user[i])
    
    
    def do_quit(s, user, name):
        msg = "
    %s 离开了聊天室" % name
        for i in user:
            if i == name:
                s.sendto(b'EXIT', user[i])
            else:
                s.sendto(msg.encode(), user[i])
        del user[name]  # 删除离开的用户
    
    
    # 接收客户端请求并处理
    def do_parent(s):
        # 用于存储用户 {'Alex':('127.0.0.1',8888)}
        user = {}
        while True:
            msg, addr = s.recvfrom(1024)
            msgList = msg.decode().split(' ')
            if msgList[0] == 'L':
                do_login(s, user, msgList[1], addr)
            elif msgList[0] == 'C':
                # "C Levi [I miss you]"
                data = ' '.join(msgList[2:])
                do_chat(s, user, msgList[1], data)
            elif msgList[0] == 'Q':
                do_quit(s, user, msgList[1])
    
    
    # 创建套接字,网络连接,创建父子进程
    def main():
        # server address
        ADDR = ('0.0.0.0', 8888)
        # 创建套接字
        s = socket(AF_INET, SOCK_DGRAM)
        s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        s.bind(ADDR)
    
        # 创建父子进程
        pid = os.fork()
        if pid < 0:
            sys.exit("创建进程失败")
        elif pid == 0:
            do_child(s, ADDR)
        else:
            do_parent(s)
    
    if __name__ == "__main__":
        main()


    客户端:

    from socket import * 
    import sys, os
    
    
    def login(s, ADDR):
        while True:
            name = input("请输入用户名:")
            msg = "L " + name
            s.sendto(msg.encode(), ADDR)
            # 接收登录结果
            data, addr = s.recvfrom(1024)
            if data.decode() == 'OK':
                print("@进入聊天室@")
                return name
            else:
                print(data.decode())
    
    
    # 发送消息
    def do_child(s, name, addr):
        while True:
            text = input("发言(quit退出):")
            # 退出
            if text.strip() == "quit":
                msg = "Q " + name
                s.sendto(msg.encode(), addr)
                sys.exit("退出聊天室")
    
            msg = "C %s %s" % (name, text)
            s.sendto(msg.encode(), addr)
    
    
    # 接收消息
    def do_parent(s):
        while True:
            msg, addr = s.recvfrom(1024)
            if msg.decode() == 'EXIT':
                sys.exit(0)
            print(msg.decode() + "
    发言(quit退出):",end="")
    
    
    # main控制套接字的创建
    def main():
        if len(sys.argv) < 3:
            print("argv is error")
            return
        HOST = sys.argv[1]
        PORT = int(sys.argv[2])
        ADDR = (HOST, PORT)
    
        s = socket(AF_INET, SOCK_DGRAM)
    
        name = login(s, ADDR)
        if name:
            pid = os.fork()
            if pid < 0:
                sys.exit("创建子进程失败")
            elif pid == 0:
                do_child(s, name, ADDR)
            else:
                do_parent(s)
        else:
            return
    
    
    if __name__ == "__main__":
        main()
  • 相关阅读:
    Docker容器启动时初始化Mysql数据库
    使用Buildpacks高效构建Docker镜像
    Mybatis 强大的结果集映射器resultMap
    Java 集合排序策略接口 Comparator
    Spring MVC 函数式编程进阶
    换一种方式编写 Spring MVC 接口
    【asp.net core 系列】6 实战之 一个项目的完整结构
    【asp.net core 系列】5 布局页和静态资源
    【asp.net core 系列】4. 更高更强的路由
    【Java Spring Cloud 实战之路】- 使用Nacos和网关中心的创建
  • 原文地址:https://www.cnblogs.com/ParisGabriel/p/9458056.html
Copyright © 2011-2022 走看看