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之前开辟的空间,子进程同样拥有,父子进程对各自空间的操作, 不会相互影响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")
eg2:
eg3":
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
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
父进程sleep(1),子进程在先
二.进程相关函数
1.os.getpid() 功能:获取一个进程的PID 返回值:返回当前进程的PID1 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())
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
3.os._exit(status) 功能:结束一个进程 参数:标书进程的终止状态
1 import os 2 3 os._exit(0)#进程退出 4 5 print("Process exit")
1 import os,sys 2 3 4 sys.exit("退出进程") 5 6 print("Process exit")
三.孤儿和僵尸 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) 优点:*非阻塞不会影响父进程运行 *使用该方法父进程可以脱离所有子进程退出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
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
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)
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)
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()
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
群聊聊天室
功能: 类似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. 管理员消息
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()
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()