Python網絡編程(子進程的建立與處理、簡單羣聊工具)

 

前言:
昨天咱們已經瞭解了多進程的原理以及它的實際使用

Unix/Linux操做系統提供了一個fork()系統調用,它很是特殊。普通的函數調用,調用一次,返回一次,服務器

可是fork()調用一次,返回兩次,由於操做系統自動把當前進程(稱爲父進程)複製了一份(稱爲子進程),而後,分別在父進程和子進程內返回。網絡

子進程永遠返回0,而父進程返回子進程的ID。這樣作的理由是,一個父進程能夠fork出不少子進程,因此,socket

父進程要記下每一個子進程的ID,而子進程只須要調用getppid()就能夠拿到父進程的ID。函數

Python的os模塊封裝了常見的系統調用,其中就包括fork,能夠在Python程序中輕鬆建立子進程ui

既然是進程那麼就會有運行和退出
接下來咱們就來了解一下進程的退出以及處理
 
孤兒進程:
    當 父進程於子進程 退出,此時 子進程就會 成爲孤兒進程
     特徵:
          孤兒進程會被系統指定進程收養,即 系統進程成爲
 這個孤兒進程 新的父進程,系統進程 會自動處理進程 退出狀態
 
殭屍進程:
    當 子進程於父進程 退出父進程沒處理子進程的 退出狀態
    此時子進程 就會成爲殭屍進程
     殭屍進程會滯留部分 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 = "\n歡迎 %s 進入聊天室" % name
    for i in user:
        s.sendto(msg.encode(), user[i])
    # 插入user
    user[name] = addr


def do_chat(s, user, name, data):
    msg = "\n{} 說: {}".format(name, data)
    for i in user:
        if i != name:
            s.sendto(msg.encode(), user[i])


def do_quit(s, user, name):
    msg = "\n%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() + "\n發言(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()
相關文章
相關標籤/搜索