websocket 单点通信,广播通信

server/2024/9/20 7:11:21/ 标签: websocket, 网络协议, 网络

        Websocket协议是对http的改进,可以实现client 与 server之间的双向通信; websocket连接一旦建立就始终保持,直到client或server 中断连接,弥补了http无法保持长连接的不足,方便了客户端应用与服务器之间实时通信。

参考:

HTML5 WebSocket | 菜鸟教程

由浅入深介绍 Python Websocket 编程-CSDN博客

应用场景:

        html单点通信

        消息群发

        聊天室功能

一,单点通信

      1,server.py

import asyncio
import websockets
from datetime import datetimeasync def handler(websocket):data = await websocket.recv()reply = f"收到数据:{data}  time: {datetime.now()}"print(reply)await websocket.send(reply)print("Send reply")async def main():async with websockets.serve(handler, "localhost", 9999):await asyncio.Future()  # run foreverif __name__ == "__main__":asyncio.run(main())

        2,python 客户端 client.py

import asyncio
import websockets
import timeasync def ws_client(url):for i in range(1, 40):async with websockets.connect(url) as websocket:await websocket.send("Hello, I'm client 1")response = await websocket.recv()print(response)time.sleep(3)if __name__ == "__main__":asyncio.run(ws_client('ws://127.0.0.1:9999'))

        3,html客户端client.html

<!DOCTYPE HTML>
<html><head><meta charset="utf-8"><title>websocket demo</title><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/css/bootstrap.min.css"><script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js">		</script><script src="https://cdn.staticfile.org/popper.js/1.15.0/umd/popper.min.js"></script><script src="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script><script type="text/javascript">async function wsClient(url) {for (let i = 1; i <= 40; i++) {const websocket = new WebSocket(url);// Wait for the WebSocket connection to openawait new Promise((resolve, reject) => {websocket.addEventListener('open', () => {resolve();});websocket.addEventListener('error', reject);});// Send a message to the serverwebsocket.send("Hello, I'm client html");// Wait for a response from the serverconst response = await new Promise((resolve) => {websocket.addEventListener('message', (event) => {resolve(event.data);});});// Print the responseconsole.log(response);// Wait for 3 seconds before sending the next messageawait new Promise(resolve => setTimeout(resolve, 1000));// Close the WebSocket connection before the next iterationwebsocket.close();}}                // Call the function with the desired WebSocket URLwsClient('ws://127.0.0.1:9999');</script></head><body></body></html>

        4,启动  :

        python server.py

        python client.py       

         

        打开client.html

二,广播消息

        1,server.py

import asyncio
import websockets
from datetime import datetime,time# 维护一个连接的客户端字典,key为remote_address
connected_clients = {}# 处理连接事件
async def connection_handler(websocket, path):remote_address = websocket.remote_addressprint(f"新连接建立 from {remote_address}")connected_clients[remote_address] = websocket  # 使用remote_address作为key添加到字典中print(f"当前连接数:{len(connected_clients)}")print(f"连接地址:{list(connected_clients.keys())}")try:while True:await message_handler(websocket)except websockets.exceptions.ConnectionClosedOK:print(f"连接已正常关闭 from {remote_address}")print(f"当前连接地址:{list(connected_clients.keys())}")finally:del connected_clients[remote_address]  # 连接关闭时从字典中移除# 处理接收到的消息,并广播出去
async def message_handler(websocket):data = await websocket.recv()reply = f"收到数据:{data}  time: {datetime.now().time()}"print(reply)# 广播消息给所有连接的客户端for client_websocket in connected_clients.values():if client_websocket != websocket:  # 避免给自己发送消息try:await client_websocket.send(reply)except websockets.exceptions.ConnectionClosedError:# 如果某个客户端连接已经关闭,由于字典的key-value特性,无需显式移除print(f"一个连接已关闭,自动从字典中移除")print("Broadcast reply sent")async def main():async with websockets.serve(connection_handler, "localhost", 9999):await asyncio.Future()  # run foreverif __name__ == "__main__":asyncio.run(main())

        2,client.py

import asyncio
import websocketsasync def ws_client(url):async with websockets.connect(url) as websocket:# 发送初始消息await websocket.send("Hello, I'm logging.")# 持续接收消息的循环while True:try:response = await websocket.recv()print(f"Received: {response}")# 可以根据需要处理接收到的消息,比如判断是否需要发送新的消息等except websockets.exceptions.ConnectionClosedError:print("Connection closed by server.")break  # 连接关闭时跳出循环except Exception as e:print(f"An error occurred: {e}")# 根据实际情况决定是否需要重连或进行其他操作# 可以在这里添加延时以控制发送或接收频率,但需谨慎使用以免阻塞事件循环# await asyncio.sleep(1)  # 示例:每秒接收一次消息if __name__ == "__main__":asyncio.run(ws_client('ws://127.0.0.1:9999'))

        3,html

<!DOCTYPE HTML>
<html><head><meta charset="utf-8"><title>websocket demo</title><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/css/bootstrap.min.css"><script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js">		</script><script src="https://cdn.staticfile.org/popper.js/1.15.0/umd/popper.min.js"></script><script src="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script><script type="text/javascript">var ws; // 在更宽的作用域定义WebSocket实例function WebSocketTest() {if ("WebSocket" in window) {ws = new WebSocket("ws://127.0.0.1:9999/handler"); // 打开WebSocket连接ws.onopen = function () {ws.send("html login");console.log("发送消息(html login)");};ws.onclose = function () {console.log("连接已关闭...");};} else {alert("您的浏览器不支持 WebSocket!");}}// 新增函数,用于发送消息function sendMessage() {if (ws && ws.readyState === WebSocket.OPEN) { // 确保WebSocket已连接且处于OPEN状态var msgInput = document.getElementById("msgInput"); // 假设有一个输入框用于输入消息var msg = msgInput.value || "默认消息内容";ws.send(msg);console.log("发送消息:" + msg);msgInput.value = ""; // 清空输入框} else {console.warn("WebSocket未连接或非OPEN状态,无法发送消息。");}}</script>
</head><body><div class="col-md-6 m-5 p-2" id="div_ws"><a class="btn btn-primary" href="javascript:WebSocketTest()">登录WebSocket</a></div><div class="col-md-6 m-5 p-2" id="div_ws"><a class="btn btn-primary" onclick="sendMessage()" >发送消息</a></div><div class="input-group input-group-lg"><div class="input-group-prepend"><span class="input-group-text" id="inputGroup-sizing-lg">消息</span></div><input type="text"   id="msgInput"  class="form-control" aria-label="Sizing example input" aria-describedby="inputGroup-sizing-lg"  placeholder="请输入消息内容..." ></div></body></html>

      4,终端启动服务端 

        python server.py

        5,打开多个终端启动多个客户端

                python client.py   

        6,打开html页面,点击登录WebSocket按钮

        7,输入框输入消息,点击发送消息

三,html 聊天室功能

        1,server.py 

        启动服务端 python server.py

    # reply = f"收到数据:{data}  time: {datetime.now().time()}" 修改响应数据reply = f"{datetime.now().time()} {websocket.remote_address} {data}"

      2,  client.html

<!DOCTYPE HTML>
<html><head><meta charset="utf-8"><title>websocket demo</title><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/css/bootstrap.min.css"><script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js">		</script><script src="https://cdn.staticfile.org/popper.js/1.15.0/umd/popper.min.js"></script><script src="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script><script type="text/javascript">var ws; // 在更宽的作用域定义WebSocket实例function WebSocketTest() {if ("WebSocket" in window) {ws = new WebSocket("ws://127.0.0.1:9999/handler"); // 打开WebSocket连接ws.onopen = function () {ws.send("html login");console.log("发送消息(html login)");};ws.onmessage = function (event) { // 添加消息接收处理器var receivedMsg = document.getElementById("receivedMsg"); // 获取用于显示消息的元素receivedMsg.innerHTML += "<br>" + event.data; // 将接收到的消息追加到元素中console.log("接收到消息:" + event.data);};ws.onclose = function () {console.log("连接已关闭...");};} else {alert("您的浏览器不支持 WebSocket!");}}// 新增函数,用于发送消息function sendMessage() {if (ws && ws.readyState === WebSocket.OPEN) { // 确保WebSocket已连接且处于OPEN状态var msgInput = document.getElementById("msgInput"); // 假设有一个输入框用于输入消息var msg = msgInput.value || "默认消息内容";ws.send(msg);console.log("发送消息:" + msg);msgInput.value = ""; // 清空输入框} else {console.warn("WebSocket未连接或非OPEN状态,无法发送消息。");}}</script>
</head><body><div class="col-md-6 m-5 p-2" id="div_ws"><a class="btn btn-primary" href="javascript:WebSocketTest()">登录WebSocket</a></div><div class="col-md-6 m-5 p-2" id="div_ws"><a class="btn btn-primary" onclick="sendMessage()">发送消息</a></div><div class="input-group input-group-lg"><div class="input-group-prepend"><span class="input-group-text" id="inputGroup-sizing-lg">消息</span></div><input type="text" id="msgInput" class="form-control" aria-label="Sizing example input"aria-describedby="inputGroup-sizing-lg" placeholder="请输入消息内容..."></div><div class="col-md-12 m-5 p-2" id="receivedMsg" style="height: 200px;overflow-y: scroll;">消息记录区:</div>
</body></html>

        3,打开3个client.html 客户端,并发送数据

       客户端1,

客户端2,

客户端3,

四,单人聊天功能分析

        1,把获取客户端字典做成api

        2,打开client.html 请求接口获取客户端字典,相当于获取在线好友列表

        3,发送消息,携带通信好友的客户端字典的key值,

        4,服务端从客户端字典查找目标连接,并发送消息


http://www.ppmy.cn/server/27732.html

相关文章

Docker容器:数据管理与镜像的创建(主要基于Dockerfile)

目录 一、Docker 数据管理 1、数据卷&#xff08;Data Volumes&#xff09; 2、数据卷容器&#xff08;DataVolumes Containers&#xff09; 二、容器互联&#xff08;使用centos镜像&#xff09; 三、Docker 镜像的创建 1、基于现有镜像创建 2、基于本地模板创建 3、基…

使用工具速记

文章目录 一、sqlyoy登录账号信息迁移二、idea导入之前的已配置的idea信息三、设置windows UI大小四、其他 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、sqlyoy登录账号信息迁移 工具(sqlyog上面菜单栏)->导入导出详情->选择要导出的账号…

Android Studio 调试:快速入门指南

作为一名Android应用开发人员&#xff0c;调试是你不可或缺的技能之一。通过调试&#xff0c;你可以定位和解决各种问题&#xff0c;包括崩溃、性能问题、UI错误等。在本文中&#xff0c;我们将分享一些实用的Android调试技巧&#xff0c;帮助你提高应用开发效率。 Android St…

GoLang Gin实际使用

所有代码同步到Admin/gitDemo - Gitee.comhttps://gitee.com/mec-deployment-team_0/git-demo/tree/dev/ 1.创建Gin框架 一般设计一个常规的web项目&#xff0c;都需要以下几个模块 runApp 主函数&#xff0c;运行整个项目routes 路由控制&#xff0c;管理跳转以及路由分组co…

kube-prometheus部署到 k8s 集群

文章目录 **修改镜像地址****访问配置****修改 Prometheus 的 service****修改 Grafana 的 service****修改 Alertmanager 的 service****安装****Prometheus验证****Alertmanager验证****Grafana验证****卸载****Grafana显示时间问题** 或者配置ingress添加ingress访问grafana…

「C++ 类和对象篇 15」C++中的私有成员

目录 一、C中私有成员的访问权限 二、私有成员的命名规范 三、C对于私有成员的访问是由编译器来检查的 一、C中私有成员的访问权限 C私有成员访问权限是基于类的&#xff0c;而不是基于对象的。 在 C 中&#xff0c;当你在类的成员函数内部访问私有成员时无论这个私有成员是属…

JavaScript基础(二)

JS语法结构——引入方式 js很明显可以是一个后缀名为js的文件&#xff0c;js的引入方式和css一样&#xff0c;也有三种方式。 1.外部 使用script表现&#xff0c;只不过增加一个src属性&#xff0c;把js文件的路径src属性中。 <script src "js文件路径">&l…

如何保证每次画出的都同一张人脸?AI绘画Stable Diffusion的Reference only教程

Ai绘画有一个很现实的问题&#xff0c;要保证每次画出的都是同一个人物的话&#xff0c;很费劲。 Midjourney就不必说了&#xff0c;人物的高度一致性一直得不到很好的解决。而在Stable Diffusion&#xff08;SD&#xff09;中&#xff0c;常用办法是通过同一个Seed值&#xf…

【PC游戏】【World of Warcraft 魔兽世界(法语版)】怀旧版 CD-ROM 安装及使用记录(一)

一、前言 正值最近暴雪和网易重修旧好之时&#xff0c;笔者在新加坡从一个宅男朋友nerd bro那里淘到了一套法语版的World of Warcraft&#xff0c;共5张CD-ROM光碟。该套光碟的发行年份是2005年&#xff0c;于欧盟制造。推荐的操作系统是Windows 2000或者Windows XP或者Mac OS…

python 笔记:cls VS self

cls&#xff1a; 用于类方法&#xff1a; cls 通常作为类方法&#xff08;用 classmethod 装饰&#xff09;中的第一个参数。它指代调用该方法的类本身&#xff0c;无论该类有没有被实例化访问类级别的属性和方法 通过 cls&#xff0c;可以访问类级别的属性和方法。可以通过 c…

[第五空间-2021]yet_another_mysql_injection

title:[第五空间 2021]yet_another_mysql_injection 查看网页源码 访问view-source:node4.anna.nssctf.cn:28356/?source 得到题目源码 <?php include_once("lib.php"); function alertMes($mes,$url){die("<script>alert({$mes});location.href{…

android 判断文件是否存在

在 Android 中&#xff0c;你可以使用 java.io.File 类来判断一个文件是否存在。下面是一个简单的示例代码&#xff1a; import java.io.File; public class FileChecker { public static boolean isFileExist(String filePath) { File file new File(fi…

【Jenkins】持续集成与交付 (五):Jenkins用户权限管理

🟣【Jenkins】持续集成与交付 (五):Jenkins用户权限管理 1、安装插件(Role-based Authorization Strategy)2、开启权限全局安全配置3、创建角色4、创建用户5、给用户分配角色6、测试权限💖The Begin💖点点关注,收藏不迷路💖 1、安装插件(Role-based Authorization …

word 表格 文字 上下居中

问题 word 表格 文字 上下居中 详细问题 笔者进行word 文档编辑&#xff0c;对于表格中的文本内容&#xff0c;如何进行上下居中&#xff1f; 解决方案 步骤1、选中需要进行操作的单元格 步骤2、右键 → \rightarrow →点击表格属性 步骤3、依次点击单元格 → \rightar…

12、Flink 的 Keyed State 代码示例

1、KeyedState 用例 import org.apache.flink.api.common.functions.AggregateFunction; import org.apache.flink.api.common.functions.ReduceFunction; import org.apache.flink.api.common.state.*; import org.apache.flink.api.common.typeinfo.TypeHint; import org.ap…

Java中的模版方法设计模式详解

Java中的模版方法设计模式详解 在Java编程中&#xff0c;设计模式是一种解决常见问题的最佳实践。其中&#xff0c;模版方法设计模式是一种行为设计模式&#xff0c;它定义了一个操作中的算法骨架&#xff0c;而将一些步骤延迟到子类中。这样可以使子类在不改变算法结构的情况…

社交媒体数据恢复:Soul

Soul数据恢复方法 在Soul这款社交软件中&#xff0c;如果您的聊天记录不小心被删除&#xff0c;是否还能恢复呢&#xff1f;以下是根据搜索结果整理出的Soul数据恢复方法。 方法一&#xff1a;使用Soul的备份和恢复功能 开启备份功能&#xff1a;首先&#xff0c;您需要确保…

PS证件照

证件照尺寸 小一寸&#xff1a;2.2cm*3.3cm 一寸&#xff1a;2.5cm*3.5cm 像素413*295 &#xff08;分辨率为300像素/英寸&#xff09; 比例5&#xff1a;7 二寸&#xff1a;3.5cm*4.9cm 二寸照相比例是4&#xff1a;3&#xff0c;像素是626*413 蓝底&#xff1a;R&a…

Java面试八股之简述Java中assert的作用

简述Java中assert的作用 Java中的assert关键字用于在代码中插入断言&#xff08;Assertion&#xff09;&#xff0c;断言是一种在开发和测试阶段用于验证程序内部状态或假设的机制。其主要作用包括&#xff1a; 条件检查&#xff1a; assert语句用于在特定代码点上检查一个布…

【prometheus】监控MySQL并实现可视化

目录 一、概述 1.1下载解压mysqld_exporter 1.2创建MySQL授权用户 1.3配置my.cnf 1.4启动mysqld_exporter 1.5prometheus配置修改 二、Grafana展示 【Prometheus】概念和工作原理介绍_prometheus工作原理 【Prometheus】k8s集群部署node-exporter 【prometheus】k8s集…