websocket携带参数(由浅入深介绍 Python Websocket 编程)
1. 为什么使用 Websocket ?
1.1 websocket 协议简介
Websocket协议是对http的改进 ,可以实现client 与 server之间的双向通信; websocket连接一旦建立就始终保持 ,直到client或server 中断连接 ,弥补了http无法保持长连接的不足 ,方便了客户端应用与服务器之间实时通信 。
适用场景 html页面实时更新, 客户端的html页面内 ,用` javascript` 与 server 建立websocket连接 ,实现页面内容的实时更新 。Websocket 非常适合网页游戏 、聊天 、证券交易等实时应用 。 要求保持长连接的实时通信的应用场景 。 如基于位置的服务应用 ,物联网 ,多方协作软件 ,在线教育 ,带社交属性的手机APP等 。实时更新数据场景 ,为什么不使用AJAX? AJAX 采用http, 如果要实时更新页面 ,则需要不断地发送http 请求,无论是否有数据更新 ,产生大量冗余通信流量 。而websocket是长连接双向通信 ,有数据更新时,服务器向客户机发送通知 。
1.2 基本原理
基于TCP ,一次握手就能建立连接 ,支持双向通信 ,可保持长连接 。
WebSocket 握手请求消息示例::
GET /chat HTTP/1.1 Host: normal-website.com Sec-WebSocket-Version: 13 Sec-WebSocket-Key: wDqumtseNBJdhkihL6PW7w== Connection: keep-alive, Upgrade Cookie: session=KOsEJNuflw4Rd9BDNrVmvwBF9rEijeE2 Upgrade: websocket如果 Server 接收连接 ,返回响应
HTTP/1.1 101 Switching Protocols Connection: Upgrade Upgrade: websocket Sec-WebSocket-Accept: 0FFP+2nmNIf/h+4BP36k9uzrYGk=响应码为101 ,表示切换为websocket 协议 。
websocket 已得到主流浏览器 ,各编程语言的广泛支持 ,基本都提供了WebSocket高阶编程API ,在一般场合下 ,可以替代socket低阶函数编程 。python 提供了更简洁的编程实现方式 。下面展示了实例代码方式 ,说明如何开发 Python websocket 服务器代码 ,python websocket 客户端, 以及javascript websocket 代码 。
2. 如何用 Python 搭建 Websocket 服务
python 第3方库 websockets 提供了websocket 实现框架 ,支持asyncio, 性能强大,稳定性好 ,可以用于生产环境。
2.1 安装websockets包
pip install websockets2.2 编写 server 端代码
Websocket服务端代码是面向多用户的长连接 ,因此本文采用了python3.7 版本的 asyncio 异步方式编写 websocket server 代码 。
服务端也可使用 ThreadPoolExecutor 线程池方式同时处理多连接的场景,用户较多时 ,性能明显不如asyncio异步方式 。
websockets 模块 server端的主要方法:
recv() 收消息 send() 发送消息 serve() 创建 server 对象实现步骤:
编写websocket 异步任务处理函数handler 创建1个websocket server 对象 异步运行 server对象websocket 地址格式:
ws://主机地址:端口号 wss://主机地址:端口号 , wss表示此连接为https 连接。下面是具体的代码 server.py
#!/usr/bin/python3 # 主要功能:创建1个基本的websocket server, 符合asyncio 开发要求 import asyncio import websockets from datetime import datetime async def handler(websocket): data = await websocket.recv() reply = f"Data received as \"{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 forever if __name__ == "__main__": asyncio.run(main())服务端handler函数代码还有1种写法 ,适用性更好 。
async def handler(websocket): async for message in websocket: reply = f"Data received as \"{message}\". time: {datetime.now()}" print(reply) await websocket.send(reply)Websocket协议本身有心跳机制 、连接检测机制 ,服务端无须关心客户端状态 ,一旦有异常 ,会自动断开连接 。
Websockets提供了交互式测试命令 ,现在可以快速测试一下服务端是否能正常工作:
(1) 启动服务器: python server.py
(2) 通过命令行连接服务端 ,并向发送hello, world 消息 ,可以看到 ,收到了服务器的响应 。 D:\workplace\python\projects\websock>python -m websockets ws://localhost:9999 Connected to ws://localhost:9999. > hello, world < Data received as "hello, world". time: 2023-04-01 09:24:14.787357 Connection closed: 1000 (OK).当然实际应用时 ,应按下面步骤来编写客户端代码 。
3. Python websocket 客户端实现代码
websockets 客户端提供的主要方法:
connect() 建立与服务器的连接 recv(), send() 收发消息 close() 显式地关闭连接下面看一下示例 client.py
import asyncio import websockets import time async def ws_client(url): for i in range(1, 40): async with websockets.connect(url) as websocket: await websocket.send("Hello, I am PyPy.") response = await websocket.recv() print(response) time.sleep(1) asyncio.run(ws_client(ws://localhost:9999))4. Javascript websocket 客户端实现代码
目前主流的浏览器都支持websocket协议 。
Javascript websocket 对象的主要属性与方法: 请参考菜鸟教程的这篇文章:https://www.runoob.com/html/html5-websocket.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"> function WebSocketTest() { text = document.getElementById("div_text"); if ("WebSocket" in window) { // 打开一个 web socket var ws = new WebSocket("ws://localhost:9999/handler"); ws.onopen = function () { // Web Socket 已连接上 ,使用 send() 方法发送数据 ws.send("Javscript发送的数据"); text.innerHTML = "数据发送中..."; alert("数据发送中..."); }; ws.onmessage = function (evt) { var received_msg = evt.data; text.innerHTML = "收到的数据:" + received_msg; alert("数据已接收..."); }; ws.onclose = function () { // 关闭 websocket text.innerHTML = "连接已关闭..."; alert("连接已关闭..."); }; } else { // 浏览器不支持 WebSocket alert("您的浏览器不支持 WebSocket!"); } } </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 border border-primary mx-5 p-2 " id="div_text" style="margin:20px;height:100px;"> display communicate text </div> </body> </html>5. 测试websocket
上述3个文件都放在同1个目录下,打开两个终端窗口 ,先运行server.py, 再运行 client,py 。
Output结果
在chrome 或edge 中运行client.html , 可以看到websocket 连接建立,发送 ,接收 ,关闭各阶段的状态 。
能够看到 ,服务器与客户端之间的通信是双向的 ,而且是长连接 ,客户端断开后 ,服务器仍然保持侦听状态 ,而且不需要accept操作 。websocket发送 、接收文件也不需要 socket 对发送窗口 buffer 进行控制 ,因此是 socket 开发非常好的替代 。注:Python异步websocket服务器最终性能与代码质量 、服务器硬件 、网络等紧密相关 ,可以使用 Websocket-benchmarker 测试工具来测试服务器 。
6. 服务器向客户端广播消息
websockets 模块支持向所有连接的客户广播消息 ,
用1个简单的例子来演示 ,实现步骤: 保存每个 websocket 客户连接 向每个客户发送消息将前面的server,.py 代码修改后如下:
#!/usr/bin/python3 # 主要功能:创建1个基本的websocket server, 符合asyncio 开发要求 import asyncio import websockets from datetime import datetime CONNECTIONS = set() async def send(websocket, message): try: await websocket.send(message) except websockets.ConnectionClosed: pass async def broadcast(message=""): # 向队列中的每个连接发送消息, 广播10次 for i in range(0,10): message = f"Broadcast: New user joined, now time is {datetime.now()}" if CONNECTIONS : # asyncio.wait doesnt accept an empty list await asyncio.wait([ asyncio.create_task(send(websocket, message)) for websocket in CONNECTIONS ]) await asyncio.sleep(30) #每次广播间隔 async def handler(websocket): CONNECTIONS.add(websocket) # 保存客户连接至集合 try: # do other things await websocket.wait_closed() finally: CONNECTIONS.remove(websocket) async def main(): async with websockets.serve(handler, "localhost", 9998): await asyncio.Future() # run forever loop = asyncio.get_running_loop() #获取当前event_loop对象 loop.create_task(broadcast()) # 添加新的异步广播任务 if __name__ == "__main__": asyncio.run(main())注: 本例broadcast() 方法每30秒 ,就向全部用户发送广播 。实际应用时可以保持永久循环
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!