功能需求
- 进入聊天室前需要输入用户名
- 有人进入聊天室会向其他用户发起通知XXX进入了聊天室
- 一个人发消息,其他人都能收到XXX说:xxxxxx
- 某个人退出聊天室,其他人也会收到通知XXX退出了聊天室
- 管理员喊话功能:管理员发言所有客户端都能收到管理员 说:XXXXX
服务端
from socket import *
import os, sys
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[name] = addr
def do_chat(s, user, name, text):
msg = '\n%-4s 说:%s' % (name, text)
# 发给除了自己外的所有人
for i in user:
if i != name:
s.sendto(msg.encode(),user[i])
def do_quit(s,user,name):
del user[name]
msg=name+'\n离开了聊天室'
for i in user:
s.sendto(msg.encode(), user[i])
# 接收客户端请求并处理
def do_child(s):
# 用于存储用户{'张三':('0.0.0.0',8888)}
user = {}
# 循环接收各个客户端请求并处理
while True:
msg, addr = s.recvfrom(1024)
msgList = msg.decode().split(' ')
# 根据msg第一项判断请求类型
if msgList[0] == 'L':
do_login(s, user, msgList[1], addr)
elif msgList[0] == 'C':
do_chat(s, user, msgList[1], ' '.join(msgList[2:]))
elif msgList[0] == 'Q':
do_quit(s,user, msgList[1])
# 发送管理员消息
def do_parent(s, addr):
while True:
msg = input("管理员消息")
msg = 'C 管理员 ' + msg
s.sendto(msg.encode(), addr)
# 创建套接字,创建链接,创建父子进程
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:
# 创建二级子进程
pid0 = os.fork()
if pid0 < 0:
sys.exit("创建进程失败")
elif pid0 == 0:
# 执行子进程功能
do_child(s)
else:
os._exit(0)
else:
os.wait()
# 执行父进程功能
do_parent(s, ADDR)
if __name__=="__main__":
main()
客户端
from socket import *
import sys, os
import signal
# 发送消息
def do_child(s, name, addr):
while True:
text = input("发言(quit退出):")
if text.strip() == 'quit':
msg = 'Q '+name
s.sendto(msg.encode(), addr)
# 从子进程中杀死父进程
os.kill(os.getppid(), signal.SIGKILL)
sys.exit("退出聊天室")
else:
msg = "C %s %s" % (name, text)
s.sendto(msg.encode(), addr)
# 接收消息
def do_parent(s):
while True:
msg, addr = s.recvfrom(1024)
print(msg.decode()+'\n发言(quit退出):', end="")
# 创建套接字,创建父子进程,登录
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)
# 登录
while True:
name = input("请输入姓名:")
msg = "L "+name
s.sendto(msg.encode(), ADDR)
data, addr = s.recvfrom(1024)
if data.decode() == 'OK':
print("登录成功")
# break
else:
print(data.decode())
continue
pid = os.fork()
if pid < 0:
sys.exit("创建进程失败")
elif pid == 0:
do_child(s, name, ADDR)
else:
do_parent(s)
if __name__=="__main__":
main()
服务端
第一个客户端登陆
第二个客户端登陆
第三个客户端登陆