写一个聊天室
功能:类似qq群聊
1.进入聊天室需要输入名字
客户端先发给服务端,服务端再发给客户端
2.有人进入聊天室会向其他人发送通知
*** 进入聊天室
3.一个人发消息,其他人会收到消息
*** 说:*****
4.某人退出聊天室,其他人也会收到通知
*** 退出了聊天室
5.管理员喊话:服务端发送消息,所有的客户端都能收到
管理员说:*****
功能模型: 转发
需要的技术:套接字通信 udp套接字
用户存储:字典或列表
消息收发的随意性
代码设计
1.封装:将每个功能封装为函数
2.接口测试(每实现一步测试一步)
代码编写流程
搭建网络连接-->创建多进程-->每个进程功能编写-->项目功能模块实现
进入聊天室
客户端: 输入名字 将信息发给服务端(L name)
等待服务端回复 根据回复判断是否登录成功
服务端: 接受请求信息 判断请求类型 判断用户名是否存在
如果不存在回复可以登录,并插入到数据结构
发送通知给其他用户
聊天
客户端:创建父子进程 发送聊天请求 /接受聊天信息
服务端:接受请求信息,将消息转发给其他客户端
客户端
# client.py
from socket import *
import sys
import os
# 接受消息
def recv_msg(s):
while True:
data, addr = s.recvfrom(2048)
if data.decode() == 'EXIT':
sys.exit(0)
print(data.decode() + '发言:', end = "")
# 发送消息
def send_msg(s, name, ADDR):
while True:
try:
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)
except KeyboardInterrupt:
msg = "Q " + name
s.sendto(msg.encode(), ADDR)
sys.exit("退出聊天室")
# 创建套接字,登录,创建子进程
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
# print('11111')
# 发送登录请求
s.sendto(msg.encode(), ADDR)
# 等待服务器回复
data, addr = s.recvfrom(1024)
if data.decode() == 'OK':
print('您已进入聊天室')
break
else:
# 不成功服务器会回复不允许登录的原因
print(data.decode())
# 创建父子进程
pid = os.fork()
if pid < 0:
sys.exit('创建子进程失败')
elif pid == 0:
send_msg(s, name, ADDR)
else:
recv_msg(s)
if __name__ == '__main__':
main()
服务端
#!/usr/bin/env python3
# coding = utf-8
'''
name:zsj
email:zhangshuaijun_py@163.com
date:2018-9
introduce:Chatroom server
env: python3.5
'''
from socket import *
import os
import sys
# 登录判断
def do_login(s, user, name, addr):
if (name in user) or name == '管理员':
s.sendto('用户名已存在'.encode(), addr)
return
else:
s.sendto('OK'.encode(), 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%s 说 %s\n' % (name, text)
for i in user:
if i != name:
s.sendto(msg.encode(), user[i])
# 退出聊天室
def do_quit(s, user, name):
msg = 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):
# 存储结构({'zhangsan':(127.0.0.1,9999)})
user = {}
while True:
msg, addr = s.recvfrom(1024)
msgList = msg.decode().split(' ')
# 区分请求类型
if msgList[0] == 'L': # 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_child(s, addr):
while True:
msg = input('管理员消息:')
msg = 'C 管理员 ' + msg
s.sendto(msg.encode(), addr)
# 创建网络,进程,调用功能函数
def main():
# server address
ADDR = ('0.0.0.0', 9999)
# 创建数据报套接字
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()