Python数据报套接字的多用户聊天室实现

  • Post author:
  • Post category:python


  1. 功能 : 类似qq群聊功能

    【1】 有人进入聊天室需要输入姓名,姓名不能重复

    【2】 有人进入聊天室,其他人会收到通知

    xxx 进入聊天室

    【3】 一个人发消息,其他人会收到消息

    xxx : xxxxxxxx

    【4】 有人退出聊天室,则其他人会收到通知

    xxx 离开了聊天室

    【5】 扩展功能:服务器可以向所有群用户发送公告

    管理员消息: xxxxxxxxxxx

  2. 确定技术模型

    【1】 消息的网络传输:socket–>udp

    【2】 发送模型 :

    转发 客户端–>服务端–>其他客户端

    【3】 服务端用户存储 {name:addr}

    【4】 收发关系处理 : 多进程分别处理消息接收和发送

  3. 整体设计

    【1】 封装方法 ,函数

  4. 注意点

    【1】 写一个模块测试一个模块

    【2】 注释的添加

  5. 具体实现

【1】 网络连接搭建

【2】 进入聊天室

1. 客户端: * 输入姓名

* 将姓名发送给服务器

* 接收反馈

* OK则进入,否则重新输入

  1. 服务端: * 接收姓名

    * 判断是否允许进入,通知客户端

    * 不允许则结束,允许则将信息保存到数据字典

    * 将登陆信息告知其他人

    【3】 实现聊天

    【4】 退出聊天室

    【5】 管理员公告

客户端

client.py

from socket import *
import os,sys 

# 服务器地址
ADDR = ('127.0.0.1',8888)

def udp_client():
    return socket(AF_INET,SOCK_DGRAM)

def login(s):
    while True:
        name = input("请输入姓名:")
        msg = "L " + name # L表示请求类型
        # 给服务器发送
        s.sendto(msg.encode(),ADDR)
        # 等待回复
        data,addr = s.recvfrom(1024)
        if data.decode() == 'OK':
            print("您已进入聊天室")
            break 
        else:
            print(data.decode())
    return name 

def send_msg(s,name):
    while True:
        try:
            text = input("发言:")
        except KeyboardInterrupt:
            text = "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) 

def recv_msg(s):
    while True:
        data,addr = s.recvfrom(2048)
        # 收到服务器EXIT则退出
        if data.decode() == 'EXIT':
            sys.exit()
        print(data.decode()+'\n发言:',end='')

def chat(s,name):
    # 创建进程
    pid = os.fork()
    if pid < 0:
        sys.exit("Error!")
    elif pid == 0:
        send_msg(s,name)
    else:
        recv_msg(s)

def main():
    s = udp_client()
    name = login(s)
    chat(s,name)

main()

服务端

server.py

# -*- coding:utf-8 -*-
"""
Chat room server
env: python3.5
exc: for socket and fork 
"""

from socket import * 
import os,sys 

# 服务端地址
ADDR = ('0.0.0.0',8888)
# 存储用户{name:addr}
user = {}

# 搭建网络连接
def udp_server():
    # 创建套接字
    s = socket(AF_INET,SOCK_DGRAM)
    s.bind(ADDR)
    return s 

def do_login(s,name,addr):
    if (name in user) or ('管理员' in  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,name,text):
    msg = "\n%s : %s"%(name,text)
    for i in user:
        if i != name:
            s.sendto(msg.encode(),user[i])

def do_quit(s,name):
    msg = "\n%s 退出了聊天室"%name 
    for i in user:
        if i != name:
            s.sendto(msg.encode(),user[i])
        else:
            s.sendto(b'EXIT',user[i])
    # 删除用户
    del user[name]

def request(s):
    while True:
        data,addr = s.recvfrom(1024)
        msgList = data.decode().split(' ')
        # 区分请求类型
        if msgList[0] == 'L':
            do_login(s,msgList[1],addr)
        elif msgList[0] == 'C':
            # 重组消息
            text = ' '.join(msgList[2:])
            do_chat(s,msgList[1],text)
        elif msgList[0] == 'Q':
            do_quit(s,msgList[1])
        
# 代码的流程控制部分
def main():
    s = udp_server()   
    # 单独创建进程发送管理员消息
    pid = os.fork()
    if pid < 0:
        print("Error")
    elif pid == 0:
        while True:
            msg = input("管理员消息:")
            msg = "C 管理员消息 " + msg 
            # 发送给父进程
            s.sendto(msg.encode(),ADDR) 
    else:
        request(s) # 接收请求


main()

核心思想:客户端、服务端建立连接;

服务端单独封装一个接受和处理请求服务,用于将客户端发来的消息,以特定协议的规则进行解析,根据解析结果调用不同函数进行特定功能执行。

客户度单独封装登陆模块,聊天模块用多进程来进行实现,子进程用于实现消息的发送,父进程用于实现消息的接收。



版权声明:本文为zsx2541577860原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。