PYTHON2.day05

1.poll方法实现IO多路复用
   p = poll()
   p.register()
   p.unregister()
   p.poll()
  
2.epoll方法实现IO多路复用
   * 效率更高
   * 触发方式更多
   * 可以监控的更多IO
  
3.struct模块
   功能:python数据转换为bytes发送
        
Struct(fmt)生成数据格式对象
pack()  将数据打包转换为bytes
unpack()  将bytes数据解析


4.本地套接字
    用于本地两个进程之间进行数据通信
   
5.多任务编程
6.进程(process)
  
*****************************************************************************************
一.基于fork的多进程编程
    1.进程的运行特征
      【1】进程可以使用计算机的多核资源
      【2】进程是计算机分配资源的最小单位
      【3】进程之间互不影响,各自独立
      【4】每个进程拥有独立的空间,各自使用自己的空间
    2.fork 使用
      pid = os.fork()
      功能:创建新的进程

      没有参数
      返回值:整数 ,如果创建进程失败,返回负数
                           如果成功,则在原有进程中返回新进程的PID,在新进程中返回0
                    
      注意:*子进程会复制父进程的全部内存空间。从fork下一句开始执行
            * 父子进程各自独立运行,运行顺序不一定,进程执行不同的内容几乎是固定搭配
            *父子进程有各自特征比如
            *父进程fork之前开辟的空间,子进程同样拥有,父子进程对各自空间的操作,
             不会相互影响
       

PYTHON2.day05
  1 import os
  2 from time import sleep
  3 
  4 pid = os.fork()
  5 
  6 if pid < 0:
  7     print("Creat pricess failed")
  8 elif pid == 0:
  9     sleep(2)
 10     print("The new process")
 11 else:
 12     sleep(3)
 13     print("The old process")
 14 
 15 print("fork test over")
fork.py

PYTHON2.day05    

eg2:

PYTHON2.day05

eg3":

PYTHON2.day05

PYTHON2.day05
  1 import os
  2 from time import sleep
  3 
  4 print("******************")
  5 a= 1
  6 
  7 
  8 pid = os.fork()
  9 
 10 if pid < 0:
 11     print("Creat process failed")
 12 elif pid == 0:
 13     print("Child process")
 14     print("a=%d"%a)
 15     a = 10000
 16 else:
 17     # sleep(1)
 18     print("Parent proess")
 19     print("a:",a)
 20 
 21 print("all a=%d"%a)
 22 
fork2.py父子进程随机

PYTHON2.day05

PYTHON2.day05
  1 import os
  2 from time import sleep
  3 
  4 print("******************")
  5 a= 1
  6 
  7 
  8 pid = os.fork()
  9 
 10 if pid < 0:
 11     print("Creat process failed")
 12 elif pid == 0:
 13     print("Child process")
 14     print("a=%d"%a)
 15     a = 10000
 16 else:
 17     sleep(1)
 18     print("Parent proess")
 19     print("a:",a)
 20 
 21 print("all a=%d"%a)
 22 
fork3.py

父进程sleep(1),子进程在先

PYTHON2.day05


二.进程相关函数
     1.os.getpid()
       功能:获取一个进程的PID
       返回值:返回当前进程的PID

PYTHON2.day05
  1 import os
  2 from time import sleep
  3 
  4 pid = os.fork()
  5 
  6 if pid <0:
  7     print("Error")
  8 elif pid == 0:
  9     print("Child PID:",os.getpid())
 10 else:
 11     print("Parent PID:",os.getpid())
getpid.py

PYTHON2.day05
     2.os.getppid()
       功能:获取父进程的PID号
       返回值:返回父进程PID   

PYTHON2.day05
  1 import os
  2 from time import sleep
  3 
  4 pid = os.fork()
  5 
  6 if pid <0:
  7     print("Error")
  8 elif pid == 0:
  9     print("Child PID:",os.getpid())
 10     print("GET parent pid:",os.getppid())#在子进程中获取父进程pid
 11 
 12 else:
 13     print("Parent PID:",os.getpid())
 14     print("Det child pid",pid)#在父进程中获取子进程pid
getppid.py

PYTHON2.day05           
     3.os.exit(status)
       功能:结束一个进程
       参数:标书进程的终止状态

PYTHON2.day05
  1 import os
  2 
  3 os._exit(0)#进程退出
  4 
  5 print("Process exit")
exit.pyPYTHON2.day05
     4.sys.exit([status])   默认0
       功能:进程退出
       参数:整数   退出状态
             字符串 表示退出时打印内容
   PYTHON2.day05
  1 import os,sys
  2 
  3 
  4 sys.exit("退出进程")
  5 
  6 print("Process exit")
sys_exit.pyPYTHON2.day05   
    
三.孤儿和僵尸
     1.孤儿进程:父进程先于子进程退出此时子进程成为孤儿进程
        * 孤儿进程会被系统进程收养,此时系统进程就会成为该进程新的父进程,
          孤儿进程退出该进程会自动处理

    2.僵尸进程:子进程先于父进程退出,父进程没有处理子进程的退出状态,此时子进程就会成为僵尸进程
        * 僵尸进程虽然结束但是会存留部分PCB在内存,大量僵尸进程会占用内存资源
     3.如何避免僵尸进程产生
       [1]:使用wait函数处理子进程退出
           pid,status = os.wait()

           功能:在父进程中阻塞等待处理子进程退出
           返回值:pid 退出的子进程的PID
                   status 子进程退出状态
           pid,status = os.Waitpid(pid,option)
           功能:在父进程中处理进程的进出状态I
           参数:pid -l 表示等待任意子进程退出
                      >0表示指定的子进程退出
                 option  0 表示阻塞等待
                         WHOHANG  表示非阻塞
           返回值:pid 退出的子进程的PID
                   status 子进程退出状态
      [2]创建二级子进程处理僵尸
           1.父进程创建爱你子进程等待回收子进程
           2.子进程创建二级子进程然后退出
           3.二级子进程成为孤儿,和原来的父进程一同执行事件
      [3]通过信号处理子进程退出
          原理:子进程退出时会发送信号给父进程,如果父进程忽略了子进程的信号,
                系统就会自动处理子进程退出
          方法:
               使用signal模块在父进程创建前写下如下语句
              
              import signal
               signal.signal(signal.SIGCHLD,signal.SIG_ING)

          优点:*非阻塞不会影响父进程运行
                *使用该方法父进程可以脱离所有子进程退出
 

PYTHON2.day05
  1 import os
  2 from time import sleep
  3 
  4 pid = os.fork()
  5 
  6 if pid ==0:
  7     print("Child PID:",os.getpid())
  8     os._exit(0)
  9 else:
 10     print("Parent process,...")
 11     while True:
 12         pass
js.py

PYTHON2.day05 

PYTHON2.day05
  1 import os
  2 from time import sleep
  3 
  4 pid = os.fork()
  5 
  6 if pid <0:
  7     print("Error")
  8 elif pid == 0:
  9     sleep(3)
 10     print("Child %d process exit"%os.getpid())
 11     os._exit(2)   #2*256 = status: 512
 12 
 13 else:
 14     pid,status = os.wait()
 15     print("pid:",pid)
 16 #print("status:",status)
 17     print("status:",os.WEXITSTATUS(status))
 18     while True:
 19         pass
wait.py


PYTHON2.day05 PYTHON2.day05

PYTHON2.day05
  1 import os
  2 from time import sleep
  3 
  4 print("******************")
  5 a= 1
  6 
  7 
  8 pid = os.fork()
  9 
 10 if pid < 0:
 11     print("Creat process failed")
 12 elif pid == 0:
 13     print("Child process")
 14     print("a=%d"%a)
 15     a = 10000
 16 else:
 17     sleep(1)
 18     print("Parent proess")
 19     print("a:",a)
 20 
 21 print("all a=%d"%a)
fork.py

PYTHON2.day05 

PYTHON2.day05
  1 import os
  2 from time import sleep
  3 
  4 pid = os.fork()
  5 
  6 if pid <0:
  7     print("Error")
  8 elif pid == 0:
  9     sleep(3)
 10     print("Child %d process exit"%os.getpid())
 11     os._exit(2)   #2*256 = status: 512
 12 else:
 13     #非阻塞等待
 14     while True:
 15         p,status = os.waitpid(-1,os.WNOHANG)
 16         if p !=0:
 17             break
 18         sleep(2)
 19         sleep(1)
 20         print("做了其他事情")
 21     while True:
 22         print("完成父进程的其他事情")
 23         sleep(2)
waitpid.py

PYTHON2.day05        

PYTHON2.day05
  1 #创建二级子进程处理僵尸
  2 import os
  3 from time import sleep
  4 
  5 def f1():
  6     sleep(3)
  7     print("元宵...")
  8 
  9 def f2():
 10     sleep(4)
 11     print("处理南北甜咸之争...")
 12 
 13 pid = os.fork()
 14 
 15 if pid <0:
 16     print("Error")
 17 elif pid ==0:
 18     p = os.fork()#创建二级子进程
 19     if p ==0:
 20         f2()#二级子进程做另一件事
 21     else:
 22         os._exit(0)
 23 else:
 24     os.wait()#等待子进程退出
 25     f1()
child_child.py

PYTHON2.day05

PYTHON2.day05
  1 import signal
  2 import os
  3 #处理子进程退出
  4 signal.signal(signal.SIGCHLD,signal.SIG_IGN)
  5 pid = os.fork()
  6 if pid < 0:
  7     print("Error")
  8 elif pid ==0:
  9     print("Child PID:",os.getpid())
 10 else:
 11     pass
 12     while True:
 13         pass
signal.py

PYTHON2.day05


群聊聊天室

功能: 类似qq微信群聊

1. 进群需要输入姓名,姓名不能重复
2. 进入聊天室会向其他人发送通知 
     xxx 进入了聊天室
3. 某人发消息群里其他人能够收到
     xxx说:xxxxxxxxxxxx
4. 某人退出聊天室也会向其他人发通知
     xxx 退出了聊天室
5. 管理员可以发送管理员消息,此时群里所有人都能收到
     管理员说:xxxxxxxx
(服务器可以向所有的用户发送)


1. 确定技术模型
    -使用字典保存用户信息{name:ip}
    - 消息发送:客户端--》服务端--》其他客户端
    - 套接字:udp套接字
    * 存储用户:字典或者列表
    - 消息收发随意:使用两个(多)进程分别处理消息收发
     

2. 注意事项
    * 设计封装方法                       将每个功能封装为函数
    * 写一个模块测试一个函数             实现一个功能,测试一个功能
    * 注释的编写添加                    
    * 流程                               搭建网络连接,逐个功能实现                              
          
          
具体实现功能

   1. 搭建网络连接
        服务端 : 创建UDP套接字,绑定地址,创建多进程

       客户端 : 创建UDP套接字,创建多0进程

   2. 用户登录
        服务端 : *  接收姓名
                  * 判断姓名是否存在
                  * 根据判断结果回复客户端
                  * 不允许登录则功能结束
                  * 允许登录将用户则加入数据结构
                  * 将用户登录提示发送给其他人


        客户端 : * 输入姓名
                  * 将姓名发送给服务器
                  * 接收服务器反馈 ,如果不允许登录则重新输入,允许则进入聊天
                  * 创建新的进程,用于消息收发


    3. 聊天
     服务端:     * 接收消息,判断请求类型
                  * 将消息转发给其用户

    客户端:     * 循环发送消息
                  * 循环接收消息

   4. 用户退出
     服务端 : * 收到q表示客户端退出
               * 将用户从user移除
           * 告知其他人xxx退出,给该用户发送退   出指令

    客户端 : * 输入q表示退出
               * 将退出信息发送给服务器
           * 发送进程退出
           * 接收进程接收到服务器指令退出

   5. 管理员消息

PYTHON2.day05
  1 #coding=utf-8
  2 '''
  3 Chatroom
  4 env:python3.5
  5 exc:socket and fork
  6 '''
  7 from socket import *
  8 import os,sys
  9 
 10 #用于存储用户{name:addr}
 11 user ={}
 12 
 13 #处理登录
 14 def do_login(s,name,addr):
 15     #判断姓名是否存在
 16     if name in user:
 17         s.sendto("该用户已存在".encode(),addr)
 18         return
 19     s.sendto(b'OK',addr)
 20 
 21     #先通知其他人
 22     msg = "欢迎%s 进入聊天室"%name
 23     for i in user:
 24         s.sendto(msg.encode(),user[i])#user[i]发送,通过键取值
 25     #将用户插入user
 26     user[name] = addr
 27 
 28 
 29 def do_chat(s,name,text):
 30     msg ="%s : %s"%(name,text)#格式
 31     #循环发送给所有人,除了自己
 32     for i in user:
 33         if i != name:
 34             s.sendto(msg.encode(),user[i])#接受UDP消息,user[i]发送,通过键取值
 35 
 36 
 37 
 38 def do_requests(s):
 39     while True:
 40         data,addr = s.recvfrom(1024)#接收UDP消息
 41         #解析请求
 42         msgList = data.decode().split(' ')#*spilt('.')方法将指定的分隔符进行拆分,拆分将会组成一个字符串的数组并返回
 43         #区分请求类型
 44         if msgList[0]=='L':
 45             do_login(s,msgList[1],addr)
 46         elif msgList[0] =='C':
 47             #重新组织消息内容
 48             text = ' '.join(msgList[2:])
 49             do_chat(s,msgList[1],text)
 50         elif msgList[0] == 'Q':
 51             do_quit(s,user,msgList[1])
 52 
 53 def do_quit(s,user,name):
 54     msg = "\n%s 退出了聊天室"%name
 55     for i in user:
 56         if i == name:
 57             s.sendto(b'quit',user[i])
 58         else:
 59             s.sendto(msg.encode(),user[i])
 60     #删除该用户
 61     del user[name]
 62 
 63 #创建网络连接
 64 def main():
 65     ADDR = ('0.0.0.0',8888)
 66     #创建套接字
 67     s = socket(AF_INET,SOCK_DGRAM)#创建数据报套接字
 68     s.bind(ADDR)#绑定地址
 69 
 70     #处理各种客户端请求
 71     do_requests(s)
 72 
 73 if __name__=="__main__":
 74     main()
chart_server.pyPYTHON2.day05
  1 from socket import *
  2 import os,sys
  3 
  4 #服务区的地址
  5 ADDR = ('172.40.71.149',8888)
  6 
  7 #发送消息
  8 def send_msg(s,name):#发消息
  9     #输入q表示退出聊天室
 10     while True:
 11         text = input("发言:")
 12         if text =="q":
 13             msg = 'Q ' + name
 14             s.sendto(msg.encode(),ADDR)
 15             sys.exit("退出聊天室")
 16         msg = "C %s %s"%(name,text)#C
 17         s.sendto(msg.encode(),ADDR)
 18 
 19 
 20 def recv_msg(s):#收消息
 21     while True:
 22         data,addr = s.recvfrom(2048)
 23         #服务器发来quit表示要退出
 24         if data.decode()=="quit":
 25             sys.exit(0)
 26         print(data.decode(),"\n发言:",end="")
 27 
 28 #创建网络连接
 29 def main():
 30     s = socket(AF_INET,SOCK_DGRAM)
 31     while True:
 32         name = input("请输入姓名:")
 33         msg = "L "+name
 34         s.sendto(msg.encode(),ADDR)#发送请求给服务端
 35         #等待回应
 36         data,addr = s.recvfrom(1024)#消息收发
 37         if data.decode() =="OK":
 38             print("您已经进入聊天室")
 39             break
 40         else:
 41             print(data.decode())#打印不允许的原因
 42 
 43     #创建新的进程,用以消息收发的随意性
 44     pid = os.fork()#创建新的进程
 45     if pid < 0:
 46         sys.exit("error创建进程失败!!")
 47     elif pid == 0:
 48         send_msg(s,name)#发送消息
 49     else:
 50         recv_msg(s)#接受消息
 51 
 52 
 53 if __name__=="__main__":
 54     main()
chart_clent.pyPYTHON2.day05
作业: 1. 完成退出功能,解决格式问题
           聊天过程中输入quit表示退出
        2. 整理fork创建进程内容
        3. 用fork创建父子进程,同时复制一个文件,各复制一半到一个新的文件中

上一篇:阶段5 3.微服务项目【学成在线】_day05 消息中间件RabbitMQ_6.RabbitMQ研究-入门程序-消费者


下一篇:阶段5 3.微服务项目【学成在线】_day05 消息中间件RabbitMQ_7.RabbitMQ研究-工作模式-工作队列模式