[Python] 多用户,多房间全双工聊天室

WebSocket实战:从零构建Node.js+Vue多人在线聊天室 实时通信是现代Web应用的核心能力之一,其关键在于解决客户端与服务器之间的双向、低延迟数据同步问题。传统HTTP协议基于请求-响应模式,难以满足即时消息、在线协作等场景的实时性要求。WebSocket协议应运而生,它在单个TCP连接上提供全双工通信通道,实现了真正的持久化双向数据交换,显著降低了延迟和网络开销。这一技术为在线客服、协同编辑、实时数据监控等应用提供了基础支撑。本文以构建一个多人在线聊天室为例,详细解析如何运用WebSocket与Socket.IO库,结合Node.js后端与Vue 3前端,实现 阅读详情

需求描述


创建一个多用户,多房间的全双工聊天室。

多用户,多房间的意思是可以有多个聊天室,每个聊天室里可以有多个用户,并且用户可以通过输入房间号进入聊天室。
全双工的意思是聊天室中的用户在接收其他用户的信息的同时,也能发送信息给其他用户。而不用等待一个用户发送完信息,等其他用户接收到之后,才能允许下个用户再次发送信息。

Python I/O多路复用


全双工功能的实现,可以通过多线程,I/O多路复用等方式,我在这边采用了I/O多路复用方案。
Python的select模块提供三种I/O多路复用的具体实现——select,poll,epoll,我在这里选用select.select(下面用select代替)。

select会监听socket或者文件描述符的I/O状态变化,并返回变化的socket或者文件描述符对象

select(rlist, wlist, xlist[, timeout]) -> (rlist, wlist, xlist)

这是Python select方法的原型,接收4个参数
rlist:list类型,监听其中的socket或者文件描述符是否变为可读状态,返回那些可读的socket或者文件描述符组成的list
wlist:list类型,监听其中的socket或者文件描述符是否变为可写状态,返回那些可写的socket或者文件描述符组成的list
xlist:list类型,监听其中的socket或者文件描述符是否出错,返回那些出错的socket或者文件描述符组成的list
timeout:设置select的超时时间,设置为None代表永远不会超时,即阻塞。

注意:Python的select方法在Windows和Linux环境下的表现是不一样的,Windows下它只支持socket对象,不支持文件描述符(file descriptions),而Linux两者都支持。

Linux下,可以通过sys.stdin标准输入流获取用户的输入,而sys.stdin就是一个文件描述符。
所以可以用下面的代码来获取用户输入

rlist, wlist, xlist = select.select( [sys.stdin], [], [] )print rlist[0].readline()

由于只监听了sys.stdin,当用户输入之后,只会返回sys.stdin对象,可以通过readline方法来获取用户输入的内容。

聊天室服务端


服务端要完成三件事:

接收多个客户端的连接

管理用户的聊天室分组

将一个客户端输入的消息广播到他所在聊天室的所有其他客户端

第一件事,定义一个list类型变量_current_in_list来表示监听多个socket连接的可读事件,利用上面说的select来处理I/O多路复用,代码如下:

rlist, wlist, xlist = select.select(_current_in_list, [], [])

当select返回时,说明rlist上有可读的socket了,这里又有两种情况:
1.如果返回的是service socket(服务器创建的socket,用来监听客户端是否连接的),表示有新的客户端连接了,调用socket.accept()方法获取新的客户端socket对象和地址(ip和port组成的元组),将新的客户端socket加入到_current_in_list。
2.如果返回的是其他socket(客户端socket),表示有客户端发送数据到服务端了,调用socket.recv()方法获取数据。

为了实现用户分组,我规定每个客户端在连接服务器之前都要先输入聊天室的房间号,并且每次发送到服务器的数据都要带上房间号,最后定义了一个dict类型的变量_room用来存储用户和房间的对应关系,客户端传递的房间号就是_room的key,而它的value则是一个客户端socket的列表。数据格式如下:

<RID:111>Welcome to Chat Room</RID:111>

对于接收到的数据,首先通过正则表达式检查是否符合规定,然后提取房间号和用户发送的消息。
判断是否新加入到聊天室的用户,如果是则发送广播通知聊天室的其他用户有新人加入,否则发送用户消息给聊天室的其他用户。关键代码如下

rgx_message = CONFORM_MSG.match(raw_message)if rgx_message:
    room_id = rgx_message.group(1)
    message = rgx_message.group(2)    if sock not in _room.setdefault(room_id, []):
        _room[room_id].append(sock)
        broadcast_message(room_id, sock, '\n[%s:%s] entered room.\n'\
                                                 % sock.getpeername())    else:
        broadcast_message(room_id, sock, \                             "\n<" +str(sock.getpeername()) + ">" + message)

根据房间号将消息广播给聊天室中除发送用户之外的所有其他用户

def broadcast_message(room_id, sock, message):
    for member in _room[room_id]:        if member is not sock:            try:
                member.send(message)            except socket.error:
                member.close()
                 _current_in_list .remove(member)
                _room[room_id].remove(member)

如果发送报错,可能socket已经被关闭,所以将它从_current_in_list和_room中删除,因为socket已经被关闭了,但还保留在_current_in_list中,select会报错。

完整的聊天室服务端代码如下:

import socketimport selectimport re

HOST = "localhost"PORT = 9898ADDR = (HOST, PORT)
BUFSIZE = 1024CONFORM_MSG = re.compile(r'^<RID:(\d+)>([\s\S]*?)</RID:\1>')


_service_socket = socket.socket()
_service_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
_service_socket.bind(ADDR)
_service_socket.listen(10)

_current_in_list = [_service_socket]
_room = dict()def broadcast_message(room_id, sock, message):
    for member in _room[room_id]:        if member is not sock:            try:
                member.send(message)            except socket.error:
                member.close()
                 _current_in_list .remove(member)
                _room[room_id].remove(member)def main():
    while True:
        rlist, wlist, xlist = select.select(_current_in_list, [], [])        for sock in rlist:            if sock is _service_socket:
                client, addr = sock.accept()
                _current_in_list.append(client)                print "Client (%s:%s) connected." % addr            else:                try:
                    raw_message = sock.recv(BUFSIZE)                    if raw_message:
                        rgx_message = CONFORM_MSG.match(raw_message)                        if rgx_message:
                            room_id = rgx_message.group(1)
                            message = rgx_message.group(2)                            if sock not in _room.setdefault(room_id, []):
                                _room[room_id].append(sock)
                                broadcast_message(room_id, sock, '\n[%s:%s] entered room.\n'\
                                                                         % sock.getpeername())                            else:
                                broadcast_message(room_id, sock, \                                                "\n<" +str(sock.getpeername()) + ">" + message)                        else:                            print "Invalid format message,", raw_message                except socket.error:                    print "Client (%s, %s) is offline" % sock.getpeername()
                    sock.close()
                    _current_in_list .remove(member)                    for room_id, socks in _room.iteritems():                        for _ in socks:                            if _ is sock:
                                _room[room_id].remove(_)                                break
                        else:                            continue
                        breakif __name__ == '__main__':
    main()

聊天室客户端


客户端也要实现三个功能:

确定房间号

根据规定的协议规则组合房间号和消息并发送给服务器

接收服务器广播的消息

客户端相对服务端的代码逻辑来的简单,房间号直接用raw_input来让用户输入获取。

用到了select I/O多路复用来实现全双工,_current_in_list中加入sys.stdin和socket,一旦用户输入或者socket接到服务器广播的消息,就返回rlist。
遍历rlist,如果是socket就通过socket.recv()接收广播消息,如果是sys.stdin则通过sys.stdin.readline()从标准输入流中获取用户输入的消息。

完整的客户单代码:

import socketimport selectimport sys

HOST = "localhost"PORT = 9898ADDR = (HOST, PORT)
BUFSIZE = 1024_current_in_list = [sys.stdin]def prompt():
    sys.stdout.write('<You> ')
    sys.stdout.flush()def gen_message(room_id, raw_message):
    return '<RID:{}>{}</RID:{}>'.format(room_id, raw_message, room_id)def main():
    room_id = raw_input('<Room ID> ')

    client_socket = socket.socket()
    client_socket.settimeout(2)    try:
        client_socket.connect(ADDR)
        _current_in_list.append(client_socket)        # notify all room's user that new client is entered
        client_socket.send(gen_message(room_id, ''))    except socket.error:        print "Unable to connect"
        sys.exit()    print 'Connected to remote host. Start sending messages'
    prompt()    while True:
        rlist, wlist, xlist = select.select(_current_in_list, [], [])        for sock in rlist:            if sock is client_socket:
                message = sock.recv(BUFSIZE)                if not message:                    print '\nDisconnected from chat server.'
                    sys.exit()                else:
                    sys.stdout.write(message)
                    prompt()            else:
                raw_message = sys.stdin.readline()
                client_socket.send(gen_message(room_id, raw_message))
                prompt()if __name__ == '__main__':
    main()

最后

以上就是小编所分享的内容,喜欢的话可以加个关注,希望能够帮助到大家,仅供参考。
我自己整理了一套最新的python系统学习教程,从基础到实战,实用性都很高,可以免费送给大家,进python学习交流群:851211580 即可免费领取。

C++后端实战:基于WebSocket的多人聊天室项目开发指南 网络编程是构建高性能服务器的基础,其核心在于处理并发连接与实时数据交换。I/O多路复用技术(如epoll)是实现高并发事件驱动的关键原理,它能有效管理大量套接字,避免传统阻塞模型的性能瓶颈。结合TCP协议,开发者可以构建稳定可靠的双向通信通道。在实时交互场景中,WebSocket协议在TCP之上提供了全双工通信能力,通过一次握手建立持久连接,非常适合在线聊天、实时通知等应用。为实现业务逻辑与数据持久化,常需集成MySQL关系型数据库进行结构化数据存储,并使用Redis内存数据库作为高速缓存以提升读写性能。数 阅读详情

相关推荐

WebSocket实战:用JavaScript和Python打造实时聊天室(附完整代码)

本文详细介绍了如何使用JavaScript和Python通过WebSocket协议构建实时聊天室,涵盖从环境搭建、核心通信机制到高级功能实现的完整流程。通过前后端分离架构(JavaScript+Python),开发者可以快速掌握WebSocket全双工通信技术,实现用户状态管理、消息历史同步等实用功能,并附有完整代码示例。

weixin_42524276的博客 385

还在怕不可以和众多女朋友一起聊天,python教你创建多窗口,再也不怕露馅了

需求描述 创建一个多用户,多房间的全双工聊天室。 多用户,多房间的意思是可以有多个聊天室,每个聊天室里可以有多个用户,并且用户可以通过输入房间号进入聊天室。 全双工的意思是聊天室中的用户在接收其他用户的信息的同时,也能发送信息给其他用户。而不用等待一个用户发送完信息,等其他用户接收到之后,才能允许下个用户再次发送信息。 Python I/O多路复用 全双工功能的实现,可以通过多线程,I/O多路复用等方式,我在这边采用了I/O多路复用方案。 Python的select模块提供三种I/O多路复用的具体实现——s

Ugyfyv的博客 414

WebSocket实战:用JavaScript和Python打造实时聊天应用(附完整代码)

本文提供了一份完整的WebSocket实战指南,教你使用JavaScript和Python构建实时聊天应用。文章详细对比了使用原生WebSocket API与Flask-SocketIO两种主流技术栈的实现方案,涵盖连接管理、消息协议、自动重连、心跳检测等核心功能,并附有完整的代码示例,帮助开发者快速掌握实时通信应用的开发要点。

weixin_29243063的博客 541

Python编写多线程多人聊天室系统(Socket编程、tkinter组件使用)

Python编写多线程多人聊天室系统,并将聊天记录存储至txt文件中(Socket编程、tkinter窗口设计),包括昵称查重、用户列表、log记录等功能。

m0_56942491的博客 1万+

手把手教会你 | 多用户-服务器聊天室应用软件开发

利用Socket、ServerSocket、DatainputStream、DataOutputStream、Thread以及GUI技术,开发一个简单聊天室应用程序。图1显示这个聊天室程序本机模拟运行后的典型结果。其中可以看到服务器接受两个用户以及显示两个用户的对话过程。

不断学习,不断进步,提高自己 1660

仿微信H5聊天室源码解析:多人群聊IM系统搭建与部署

即时通讯(IM)已渗透到社交、客服、社群运营等众多业务场景。实现一个可落地的聊天系统,关键在于消息的实时推送与可靠存储。WebSocket作为全双工通信协议,是构建多人群聊、消息广播的核心技术底座。从账号体系到消息模型,从在线状态到离线消息同步,每一个环节都影响用户体验。本文将基于一套仿微信界面的H5聊天室源码,剖析IM系统的真实技术栈,涵盖WebSocket连接管理、群聊消息链路、历史记录分页、离线消息增量同步,以及交友与客服双模式的架构设计。同时提供从云服务器部署到Nginx反向代理、HTTPS证书配置

weixin_34318272的博客 516

Python实现多用户全双工聊天(一对一)

多用户全双工聊天简陋版 简单实现了两个客户端之间的通信,客户端发送消息,先由服务器接收,然后服务器转发到另一客户端。 该版本功能非常简陋,仅仅实现了最简单的聊天,有很多地方需要注意。 工作步骤: 服务器端运行 一个客户端运行,连接成功后输入用户名,服务器会保存该用户名在一个字典中,字典的对应关系是 username --> socket 输入用户名之后,该客户端需要确定一个聊天用...

dixinheba57619的博客 1020

python 脚本示例 全双工多用户

python 脚本示例 全双工多用户

python实现多人聊天室_[Python] 多用户,多房间全双工聊天室

需求描述创建一个多用户,多房间的全双工聊天室。多用户,多房间的意思是可以有多个聊天室,每个聊天室里可以有多个用户,并且用户可以通过输入房间号进入聊天室。全双工的意思是聊天室中的用户在接收其他用户的信息的同时,也能发送信息给其他用户。而不用等待一个用户发送完信息,等其他用户接收到之后,才能允许下个用户再次发送信息。Python I/O多路复用全双工功能的实现,可以通过多线程,I/O多路复用等方式,我...

weixin_39686230的博客 634

多人聊天室python实现

我的python课设,绝对可以运行的,注意如果运行出错要查看自己的python环境有没有弄好,里面代码有wx包,注意下载对应版本的wx包。

Python网络编程基于多线程实现多用户全双工聊天功能示例

本文实例讲述了Python网络编程基于多线程实现多用户全双工聊天功能。分享给大家供大家参考,具体如下: 在前面一篇《Python网络编程使用select实现socket全双工异步通信功能》中,我们实现了1对1的异步通信,在文章结尾,给出了多对多通信的思路。 既然说了,咱就动手试一试,本次用的是多线程来实现,正好练练手~ 首先讲一下思路: 我们将服务器做为中转站来处理信息,一方面与客户端互动,另一方面进行消息转发。 大体思路确定下来后,需要确定一些通信规则: 1. 客户端与服务器建立连接后,需要输入用户名登入,若用户名已存在,将reuse反馈给用户,用户输出错误信息,退出 2. 用户输入正确的用

python多台电脑聊天室_[Python] 多用户,多房间全双工聊天室

需求描述创建一个多用户,多房间的全双工聊天室。多用户,多房间的意思是可以有多个聊天室,每个聊天室里可以有多个用户,并且用户可以通过输入房间号进入聊天室。全双工的意思是聊天室中的用户在接收其他用户的信息的同时,也能发送信息给其他用户。而不用等待一个用户发送完信息,等其他用户接收到之后,才能允许下个用户再次发送信息。Python I/O多路复用全双工功能的实现,可以通过多线程,I/O多路复用等方式,我...

weixin_39795419的博客 186

Multisim电路仿真与实验:包含连续时间系统的模拟、周期信号频谱分析、组合逻辑电路:竞争冒险、数字锁

这些实际系统可以是电的或非电的物理量系统,也可以是经济、军事等非物理量系统。矩形周期信号的幅度和周期保持不变,随着占空比的增加(即脉宽加大),主峰高度越高,主峰宽度越窄,各谱线间隔越窄,主峰包含的谱线数量越少,有效频带宽度越窄,主峰内高次谐波分量越少。输入信号所经路径不同而引起的冒险现象,按短暂尖峰极性的不同,可将冒险分为“0-1-0”型险象(也称“1”型)和“1-0-1”型险象(也称“0”型)理想的正弦波的幅度谱包含1条谱线,证明其只有基频分量,而无谐波分量,如果能测出谐波分量,说明该正弦波已有失真。

Jumpingtigger_35的博客 1万+

【python网络编程】多线程实现多用户全双工聊天

多对多全双工聊天通信 我们将服务器做为中转站来处理信息,一方面与客户端互动,另一方面进行消息转发。 大体思路确定下来后,需要确定一些通信规则: 1. 客户端与服务器建立连接后,需要输入用户名登入,若用户名已存在,将reuse反馈给用户,用户输出错误信息,退出 2. 用户输入正确的用户名后,即可进行通信了。如果未选择通信对象,则服务器会反馈信息,提示用户选择通信对象 3. 选择通信对象的方法为,输入to:username,如果所选择的对象不存在,反馈错误信息,

jiange_zh的博客 9043

Unity性能优化 - 动态图集

Unity 动态图集是 Unity 引擎中用于处理游戏纹理优化的一种技术。本文介绍了什么是动态图集,动态图集的优缺点,以及提供了一套完整的代码供大家参考。

qq_33808037的博客 4840

PCIE总线及其PCIE SWITCH的使用

PCIe总线与PCI最大的区别在工作原理上,PCIe是采用点到点的串行方式进行传输的,被称为“串行PCI”,由于采用了串行方式传输使得其工作频率可以达到2.5Ghz,大大增加了传输速率,同时采用全双工的通信方式,使得其传输速度提高了一倍,每一个PCIe总线设备与外部通信时有四根数据总线,分别有两个RX和TX,两根用于发送,两根由于接收。PCIe switch中文翻译为PCIe开关或PCIe交换机,主要作用将PCIe设备互联,PCIe switch芯片与其设备的通信协议都是PCIe;

zxm8513优快云博客 4734

python核心编程课后作业2-10

多用户全双工多房间。这次实现了房间功能,删掉了私聊的功能。本来想同时实现的,发烧脑子烧坏了,代码写的又臭又长,难过。 参数介绍:服务器存储account(用户),以及chat_room(聊天室)的信息。其中account中存储了用户名,用户socket以及用户所在房间(没有进入房间则为None)。chat_room中存储了聊天室的id,以及所有进入聊天室的用户名称。 思路:客户端连接后,存储用

ZERO 272

为什么我的 Spark Executor / Yarn Container 只有一个vCore?

在很多集群里,在关闭了Spark的DynamicAllocation的前提下(避免自动申请空闲资源,干扰测试结果),都会观察到:提交Spark作业时,申请 1 个 driver + n 个 executor 会在Yarn上会对应创建 n+1 个 container,但是每个container只有一个vCore,通过--driver-cores和--executor-cores设定的vCore数都未奏效。有人会因此错误的认为Yarn上的一个vCore对应一个Spark的executor,这是非常错误的,正确的

Laurence的技术博客 916

标题:30 分钟带你跑完 TCP 聊天室——Python 网络编程全景入门 #Day5

大家好,我是。今天把网络编程所有知识点浓缩成「一张脑图 + 两段代码 + 一张对照表」,保证你 30 分钟内能独立写出一个可运行的。明晚我们直接升级多进程 / 多线程并发版本!

2201_75887467的博客 689

C#上位机面试必问30题:机器视觉+工业通信+性能优化(含答案解析)

工业通信:围绕“稳定性”展开,重点答异常处理、断线重连、资源释放;机器视觉:围绕“精度+速度”展开,重点答预处理、ROI、参数优化;性能优化:围绕“UI不卡、内存不泄、效率不低”展开,重点答异步、限流、资源复用;面试加分项:结合实际项目案例(如“我在XX项目中用信号量解决了多设备并发卡顿问题”)。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1508

推荐系统[九]项目技术细节讲解z2:搜索Query理解[Term Weight、Query 改写、同义词扩写]和语义召回技术

在 pooling 方法上,我们也尝试过不同层 pooling,或者在多层上增加 attention 聚合 BERT 多层结果,效果和最后一层所有 token 做 average pooling 相当。损失函数方面,在排序阶段使用pairwise比 pointwise 损失函数的效果会更好。

丨汀、的博客 1109

从零开始用Python搭建WebSocket服务:websockets库实战教程(含异步IO详解)

本文是一份基于Python websockets库构建WebSocket服务的实战教程。文章从异步IO(asyncio)核心概念讲起,详细指导如何从零搭建一个高性能的WebSocket服务器,并通过一个多房间实时聊天室的完整案例,深入探讨了连接管理、消息广播、心跳机制及生产环境下的性能优化与扩展方案。

qsc90123456的博客 440

一文搞懂数据标注

数据标注即通过分类、画框、标注、注释等,对图片、语音、文本等数据进行处理,标记对象的特征,以作为机器学习基础素材的过程。简言之,就是需要给数据做好标注,这样机器就能知道学习哪些内容。

女王の专属领地 9933

Web开发实战:Fish-Speech-1.5语音聊天室搭建

本文介绍了如何利用星图GPU平台自动化部署Fish-Speech-1.5镜像,快速搭建一个实时语音聊天室。该方案基于Fish-Speech-1.5强大的语音合成与克隆能力,用户输入文字并选择声音后,即可实时生成并播放个性化语音,为在线社交、游戏等场景提供生动有趣的语音交互体验。

weixin_42452924的博客 121

Open Dynamics Engine(ODE)物理引擎教程(1)--下载与安装准备

引言 ODE是一个免费的工业级库,用于模拟关节式刚体动力学,例如地面车辆、腿部生物和虚拟现实环境中的移动对象。它具有快速、灵活、健壮和独立于平台的特点,具有先进的关节、摩擦接触和内置的碰撞检测功能。 项目主页:http://www.ode.org/ 线上手册:http://ode.org/wiki/index.php?title=Manual 本教程为博主亲自亲测过程的总结,转载请注明出处! ...

腿足机器人领域 2216

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Python 的 WebSocket 实现详解

WebSocket是一种全双工TCP通信协议,通过HTTP握手升级建立持久连接,实现浏览器与服务器的实时双向通信,显著降低延迟。Python可通过websocket-client或websockets库实现WebSocket连接,后者支持异步操作适用于服务器开发。实际应用需处理连接异常并实现心跳机制保持稳定,与Socket.IO不同,WebSocket是标准协议。该技术广泛应用于实时监控、在线聊天、游戏服务等需要即时数据交换的场景。

Datafox(数据狐199-7010-8018) 296

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

上一篇: Python 使用 Beanstalkd 做异步任务处理
下一篇: Python中str()、repr()、``的区别
python-花猫
博客等级 码龄7年 105粉丝 179原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值