由浅入深介绍 Python Websocket 编程
为什么使用 Websocket ?
websocket 协议简介
Websocket协议是对http的改进,可以实现client 与 server之间的双向通信。弥补了http单向通信而且无法保持长连接的不足,方便了客户端应用与服务器之间实时通信。
适用场景:
- html页面实时更新, 这时Client端为html页面,用javascript与 server 建立websocket连接,实现实时通信,
- 适合python用异步方式开发高性能,保持长连接的通信程序。
为什么不使用TCP?
websocket 基于http, 可提供更健壮的连接状态管理机制,同时可方便地支持TLS/SSL等加密方式,而使用TCP方式,虽然开销小了很多,但编程工程量与复杂度显然大了许多。
基本原理
基于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=
websocket 已得到主流浏览器,各编程语言的广泛支持,基本都提供了WebSocket高阶编程API,在一般场合下,可以替代socket低阶函数编程。python 提供了更简洁的编程实现方式。下面展示了实例代码方式,说明如何创建 Python websocket 服务器,python客户端 ,与javascript 客户端, 实现 websocket 通信。
如何用 Python 搭建 Websocket 服务
python 第3方库 websockets 提供了websocket 实现框架,支持asyncio, 性能强大,稳定性好,可以用于生产环境。
第1步,安装
pip install websockets
第2步,搭建server
为了提高性能,本文采用了python3.7 版本的 asyncio 异步方式编写 websocket server 代码。
也可以在服务端使用 ThreadPoolExecutor 线程池方式来提供提供同时处理多个连接的场景,但asyncio异步方式性能更好。
websockets 模块 server端的主要方法:
- recv() 收消息
- send() 发送消息
- serve() 创建 server 对象
实现步骤:
1) 编写websocket 异步任务处理函数handler
2) 创建1个websocket server 对象
3) 异步运行 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())
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'))
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>
测试通信
上述3个文件都放在同1个目录下,
打开两个终端窗口,先运行server.py , 再运行 client,py
能够看到,服务器与客户端之间的通信是双向的,而且是长连接,客户端断开后,服务器仍然保持侦听状态,而且不需要accept操作,发送接收大文件也不需要 socket 发送窗口 buffer 进行控制,因此也是 socket 开发非常好的替代。
输出为:
在chrome 或edge 中运行client.html, 可以看到websocket 连接建立,发送,接收,关闭各阶段的状态。
服务器向所有客户端广播消息
websockets 模块支持向所有连接的客户广播消息,
步骤为:
- 保存websocket 对象
- 用broadcast() 方法发送广播
将前面的server,.py 代码修改后如下:
#!/usr/bin/python3
# 主要功能:创建1个基本的websocket server, 符合asyncio 开发要求
import asyncio
import websockets
from datetime import datetime
CONNECTIONS = set()
async def handler(websocket):
global CONNECTIONS
try:
message = f"Broadcast: now time is {datetime.now()}"
websockets.broadcast(CONNECTIONS, message)
data = await websocket.recv()
reply = f"Data received as "{data}". time: {datetime.now()}"
print(reply)
await websocket.send(reply)
print("Send reply")
finally:
CONNECTIONS.remove(websocket)
async def main():
async with websockets.serve(handler, "localhost", 9998):
await asyncio.Future() # run forever
if __name__ == "__main__":
asyncio.run(main())
相关文章
- 2020东京奥运秘密武器曝光:AI不仅负责计时,还逼这些裁判失业
- Waymo又添新兄弟:Google孵化intrinsic,用AI训练机器人
- 高效程序员的7个习惯——来自一位前谷歌技术主管
- 互联网企业忙加薪,背后的原因是……
- 算法一看就懂之「 数组与链表 」
- 2019中国互联网企业100强榜单:阿里登顶
- 我答"编程为什么不用中文?":中文API的意义和探索
- 系统管理员必读的容器入门指南
- 程序员离职2个月,前领导要求他回去改代码,网友:收费5千一次
- 月薪3万的程序员,脱单这么难?
- 让开发人员变平庸的八个习惯,看看你中了几条
- AI复活!用GPT-3复刻逝去未婚妻,美国小哥让挚爱以数字形态永生
- 编程大赛自曝泄密境外,槽点越扒越多,官方直接关评论
- AI救援河南洪灾!CMU博士生开发「求助地图」,时空精准可视化
- AI加持的WPS来了:金山开源办公DL框架KSAI-Lite
- GitHub免费支持CI/CD了,测试部署高度自动化
- 霍尼韦尔和剑桥量子达成新里程碑 离量子计算规模化更进一步
- 干货 | 从零开始配置前端开发环境
- 喜大普奔!刚刚,华为鸿蒙正式发布!
- 不容错过的 Github万星程序员面试宝典