WebSocket
Prerequisite
背景:我有个微信机器人,源码主体采用的是 WebSocket 库,群友有个需求,就是需要机器人在重要的日期之前提醒,我的方案是让它在重要的日期前一天早上 8 点和晚上 10 点进行提醒。实现过程比较简单,采用多进程(两个就够了),一个负责接收群友的消息并传递对应信息,另一个负责日期提醒
简单来说,WebSocket 是一个持久化的协议(如下图所示),先基于 HTTP 协议建立连接,再进行信息传递,此时信息传递是双向的,即客户端 ⇄ 服务器
参考文章:看完让你彻底搞懂Websocket原理
参考视频:WebSocket打造在线聊天室【第一集】
参考视频:How to Create a WebSocket Server in Python - Fun with WebSockets
参考文章:websocket-client(websocket)和websockets区别
python 库
python 的 WebSocket 有两个库,分别是 websocket-client 和 websockets。websocket-client 是一个 websocket 服务的 client 端模块,websockets 是一个模块有 server 端和 client 端,一般来前者(websocket-client)是同步的,后者(websockets)是异步的,因此后者常与协程(asyncio)一起用
下载方法分别是 pip install websocket-client
和 pip install websockets
websocket-client
我的微信机器人使用的 websocket-client 大致如下:
#! /usr/bin/env python
# -*- coding: UTF-8 -*-
import websocket
import time
websocket._logging._logger.level = -99
ip = '127.0.0.1'
port = 5555
SERVER = f'ws://{ip}:{port}'
def output(msg):
now = time.strftime("%Y-%m-%d %X")
print(f'[{now}]:{msg}')
def on_open(ws):
ws.send("Hello")
def on_message(ws, message):
print(message)
def on_error(ws, error):
output(f'on_error:{error}')
def on_close(ws):
output("closed")
if __name__ == '__main__':
ws = websocket.WebSocketApp(SERVER, on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close)
此时只有一个进程,用于接收群友的消息并传递对应信息,于是我加了第二个进程后代码如下:
#! /usr/bin/env python
# -*- coding: UTF-8 -*-
import websocket
import time
from multiprocessing import Process
websocket._logging._logger.level = -99
ip = '127.0.0.1'
port = 5555
SERVER = f'ws://{ip}:{port}'
def output(msg):
now = time.strftime("%Y-%m-%d %X")
print(f'[{now}]:{msg}')
def on_open(ws):
ws.send("Hello")
def on_message(ws, message):
print(message)
def on_error(ws, error):
output(f'on_error:{error}')
def on_close(ws):
output("closed")
def reminder():
pass
if __name__ == '__main__':
ws = websocket.WebSocketApp(SERVER, on_open=on_open, on_message=on_message, on_error=on_error, on_close=on_close)
# ws.run_forever()
p1 = Process(target=reminder)
p2 = Process(target=ws.run_forever)
p1.start()
p2.start()
相比于之前的代码只是多出了 reminder 函数,且改成了多进程,这样就能同时实现两个功能(reminder 和 run_forever),且互不干扰 ~
websockets
参考代码:python-websockets(By ParametricCamp)
服务端代码
#! /usr/bin/env python
# -*- coding: UTF-8 -*-
import websockets
import asyncio
PORT = 7890
print("Server listening on Port " + str(PORT))
# 接收 clients,且不重复
connected = set()
async def echo(websocket, path):
print("A client just connected")
# 一个 client 加入
connected.add(websocket)
# 处理接收的消息
try:
async for message in websocket:
print("Received message from client: " + message)
# 向发送消息的 client 发送同样的消息
for conn in connected:
if conn == websocket:
await conn.send("Someone said: " + message)
# 处理取消连接的 client
except websockets.exceptions.ConnectionClosed as e:
print("A client just disconnected")
finally:
connected.remove(websocket)
# 开始工作
start_server = websockets.serve(echo, "localhost", PORT)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
客户端代码
#! /usr/bin/env python
# -*- coding: UTF-8 -*-
import websockets
import asyncio
async def listen():
url = "ws://127.0.0.1:7890"
# 连接服务器
async with websockets.connect(url) as ws:
# 发送消息
await ws.send("Hello Server!")
# 等待接收服务端消息
while True:
msg = await ws.recv()
print(msg)
# 开始连接
asyncio.get_event_loop().run_until_complete(listen())
示例图如下: