实现聊天功能
1什么存储用户:列表
2什么类型套接字:udp
3怎么发消息:转发 即客户端发给服务器,
然后在送给其他人
*在客户端让发送和接收相互独立,各使用
一个进程
*在服务端,管理员喊话和请求接收需要独立
各使用一个进程
注意点:
1功能封装,将每个功能模块化
2测试,每个功能进行测试
#代码编写流程
#搭建网络连接---->>>多进程的创建----->>
#每个进程功能的编写----->>>项目功能的逐个编写
'''
客户端可能发送的请求:
1进入聊天室:'L+name'
服务器:1识别请求2判断是否可以进入
3回复客户端4保留用户5告知其他人谁退出
2聊天请求:'C+name+消息'
服务器:1识别请求2转发给其他人
3退出聊天室'Q name'
服务器:1识别请求2告知其他人谁退出
3将其从用户中删除
'''#1搭建网络连接
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
s.sendto(b'OK',addr)
#通知所有人
msg = '\n欢迎 %s 进入聊天室'%name
for i in user:
s.sendto(msg.encode(),user[i])
#将用户插入字典
user[name] = addr
#print('登录')
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])
#print('聊天')
def do_quit(s,user,name):
del user[name]
msg = '\n'+ name + '离开了聊天室'
for i in user:
s.sendto(msg.encode(),user[i])
#print('退出')
#接收客户端请求并处理
def do_child(s):
#用于存储用户{'z':(172.60.50.51,8956)}
#可变类型,修改可被影响
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':
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)
#1创建套接字,创建连接,创建父子进程
def main():
#server address
ADDR = ('0.0.0.0',10010)
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()客户端可能发送的请求:
1进入聊天室:'L+name'
服务器:1识别请求2判断是否可以进入
3回复客户端4保留用户5告知其他人谁退出
2聊天请求:'C+name+消息'
服务器:1识别请求2转发给其他人
3退出聊天室'Q name'
服务器:1识别请求2告知其他人谁退出
3将其从用户中删除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('error')
return
HOST = sys.argv[1]
PORT = int(sys.argv[2])
ADDR = (HOST,PORT)
s = socket(AF_INET,SOCK_DGRAM)
#登录
while True:
name = input('请输入姓名:')
#L里面要加空格,按空格切割
msg = 'L ' +name
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:
print('创建子进程失败')
elif pid == 0:
#连接函数do_child实现一次运行
do_child(s,name,ADDR)
else:
#连接函数do_parent实现一次运行
do_parent(s)
if __name__ == '__main__':
main()