这是客户端:
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
tokenList = {}
@app.route("/test")
def testConnect():
sid = tokenList["114514"]
print(sid)
emit('newMission', {'from': 'server'}, namespace="newMission", to=sid)
return 'success'
@socketio.on('register')
def register(data):
print("服务器%s 尝试注册" % data["secret"])
emit('register', {"token": "1919810"})
print("注册成功,token 为%s" % "1919810")
sid = request.sid
join_room("mainRoom")
tokenList["114514"] = sid
"""@socketio.on('message')
def message(data):
print(data) # {'from': 'client'}
emit('response', {'from': 'server'})
sleep(5)
emit('response', "exit")"""
if __name__ == '__main__':
socketio.run(app, debug=True, host='127.0.0.1', port=8090)
这是服务端:
import socketio
sio = socketio.Client()
@sio.event()
def connect():
print('正在注册,请稍后')
sio.emit('register', {'secret': '114514'})
@sio.on('register')
def isReg(data):
print("注册成功,当前 token 为" + data["token"])
@sio.on("newMission", namespace="message")
def newMission(data):
print(data)
sio.connect('ws://localhost:8090')
sio.wait()
预计是打算让服务端获取客户端的 sid ,然后通过 sid 推送消息,但是不知道为什么客户端死活无法收到消息,查了很久也不知道哪里写错了,求助万能的 v 友
1
BeautifulSoup 2022-07-13 15:32:06 +08:00
请问你现在的部署环境是怎么样的?如果是开发环境,flask 自带的应用服务器是不支持 ws 协议的,需要通过前端 socketio.js 库降级为轮询使用
|
2
zhishixiang OP @BeautifulSoup 服务端安装了 gevent-websocket 库,可以使用 websocket ,客户端用的是 python websocket 库
|
3
NessajCN 2022-07-13 16:31:53 +08:00
服务端不是这么写的
sio.connect()是客户端连服务器端的函数 服务器端要用 aiohttp 之类的库部署 https://python-socketio.readthedocs.io/en/latest/server.html#deployment-strategies |
4
zhishixiang OP @NessajCN 才发现服务端和客户端代码写反了,得换过来看
|
5
NessajCN 2022-07-13 17:21:26 +08:00
@zhishixiang 那你服务器端那边的 emit 之类的函数要放在 socketio 实例里啊
socketio.emit() |
6
zhishixiang OP @NessajCN 已经导入过了,可以直接使用 emit ,忘记打出来了,我放完整源码吧
``` from hashlib import new from re import T from time import sleep import requests from flask import Flask, sessions, request import pymysql import json from flask_socketio import SocketIO, emit, join_room, leave_room, send db = pymysql.connect(host="gz-cynosdbmysql-grp-pre3qflf.sql.tencentcdb.com", port=21297, user="autowhitelist", password="nl6a0j2pcaLDIXcb", database="autowhitelist") cursor = db.cursor() app = Flask(__name__) # 以下是轮询方案,能不用就尽量不用 """@app.route("/checkNew") def checkNew(): secret = request.values.get("secret") newMission = cursor.execute("SELECT * FROM missionList WHERE secret=%s AND isEnd = 0", secret) print(newMission) if newMission == 0: return json.dumps({"status": "0", "msg": "No new mission"}) else: mission = cursor.fetchone() id = mission[1] cursor.execute("UPDATE missionList SET isEnd = 1 WHERE id = %s", id) db.commit() return json.dumps({"status": "1", "msg": "New Whitelist", "id": id})""" """@app.route("/uploadNew") def uploadNew():""" """@app.route("/checkServer") def checkServer(): secret = request.values.get("secret") isReg = cursor.execute("SELECT * FROM registerList WHERE secret=%s", secret) if isReg == 1: return ("Success") else: return ("Server not exist") """ app = Flask(__name__) app.config['SECRET_KEY'] = 'secret!' socketio = SocketIO(app) tokenList = {} @app.route("/test") def testConnect(): sid = tokenList["114514"] socketio.emit('newMission', {'from': 'server'}, namespace="newMission", to=sid) return "success" @socketio.on('register') def register(data): print("服务器%s 尝试注册" % data["secret"]) emit('register', {"token": "1919810"}) print("注册成功,token 为%s" % "1919810") sid = request.sid join_room("mainRoom") tokenList["114514"] = sid """@socketio.on('message') def message(data): print(data) # {'from': 'client'} emit('response', {'from': 'server'}) sleep(5) emit('response', "exit")""" if __name__ == '__main__': socketio.run(app, debug=True, host='127.0.0.1', port=8090) ``` |
7
zhishixiang OP @zhishixiang 又忘记删机密信息了,改个密码先
|
8
NessajCN 2022-07-13 18:05:38 +08:00
@zhishixiang 我大概知道啥问题了。你客户端里是 sio.connect("ws://localhost:8090"),
但 socketio 虽然是用 websocket 实现的,你在连接的时候却不能这么写 你改成 sio.connect("http://localhost:8090") 试试 |
9
zhishixiang OP @NessajCN 还是不行,而且会弹出报错(虽然没什么影响)
message handler error Traceback (most recent call last): File "C:\Users\86177\AppData\Local\Programs\Python\Python310\lib\site-packages\engineio\server.py", line 622, in _trigger_event return self.handlers[event](*args) File "C:\Users\86177\AppData\Local\Programs\Python\Python310\lib\site-packages\socketio\server.py", line 730, in _handle_eio_message pkt = packet.Packet(encoded_packet=data) File "C:\Users\86177\AppData\Local\Programs\Python\Python310\lib\site-packages\socketio\packet.py", line 41, in __init__ self.attachment_count = self.decode(encoded_packet) File "C:\Users\86177\AppData\Local\Programs\Python\Python310\lib\site-packages\socketio\packet.py", line 111, in decode self.data = self.json.loads(ep) File "C:\Users\86177\AppData\Local\Programs\Python\Python310\lib\json\__init__.py", line 346, in loads return _default_decoder.decode(s) File "C:\Users\86177\AppData\Local\Programs\Python\Python310\lib\json\decoder.py", line 337, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "C:\Users\86177\AppData\Local\Programs\Python\Python310\lib\json\decoder.py", line 355, in raw_decode raise JSONDecodeError("Expecting value", s, err.value) from None json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0) |
10
NessajCN 2022-07-13 19:10:05 +08:00
@zhishixiang 这个报错就说明消息成功传达,只是参数读取出问题吧?譬如你传过去的{'secret':'114514'}这个参数直接 data["secret"]不行,那就 json.loads(data)再 data["secret"]
|
11
zhishixiang OP @NessajCN 已经读取成功了,这个报错是服务端的,完全不影响使用,而且是不知道改了什么才出现的,根本修复不了,很奇怪
|
12
NessajCN 2022-07-13 19:22:33 +08:00
@zhishixiang 那不对啊,你贴的报错信息是 socketio 这个包(也就是 python-socketio ,你客户端用的就是这个)的。但你服务器端代码用的是 flask_socketio 。报错怎么会报没在用的包的错?
|
13
zhishixiang OP @NessajCN 会不会是 flask socketio 封装了 socketio 的相关内容
|
14
raycool 2022-07-13 23:05:41 +08:00
直接用 tornado 或者 fastapi
|
15
zoofy 2022-07-14 16:27:31 +08:00 1
给你贴个可以运行的代码
``` 服务端 from flask import Flask from flask_socketio import SocketIO, emit, join_room from loguru import logger app = Flask(__name__) app.config['SECRET_KEY'] = 'secret!' socketio = SocketIO(app) tokenList = {} @app.route("/test") def testConnect(): sid = tokenList["114514"] data = {'from': 'server'} # socketio.emit('newMission', data, namespace='/mission') socketio.emit('newMission', data, to=sid, namespace='/mission') return 'success' @socketio.on('register') def register(data): sid = data['sid'] print("服务器%s 尝试注册" % data["secret"]) emit('register', {"token": sid}) logger.debug("注册成功,token 为%s" % sid) join_room(sid) tokenList["114514"] = sid @socketio.on('message', namespace='/mission') def message(data): print(data) # {'from': 'client'} # emit('response', {'from': 'server'}) # sleep(5) # emit('response', "exit") if __name__ == '__main__': socketio.run(app, debug=True, host='127.0.0.1', port=8090) ``` ``` 客户端 import socketio sio = socketio.Client() @sio.on('connect') def on_connect(): print('正在注册,请稍后') # 获取 namespace sid, 发送给 server sid = sio.get_sid('/mission') sio.emit('register', {'secret': '114514', 'sid': sid}) @sio.on('register') def isReg(data): print("receiver message from register ,当前 token 为", data['token']) pass @sio.on("newMission", namespace='/mission') def newMission(data): print("get mission message data: ", data) sio.connect('ws://localhost:8090') sio.wait() ``` 主要是获取到 namespace 后,把 namespace sid join room, 记录好 sid. 要 emit 的时候, 加上 namespace 和 roomId(sid)进行发送 |
16
zhishixiang OP @zoofy 还是不行,而且有几个问题:
1.sid 应该用 request.sid 获取,data 数据里面没有 sid 内容 2.目的是为了当访问 localhost:8090/test 时客户端能收到消息,但是仍未实现 3.要是实在无法解决的话有没有别的方法能实现 websocket 连接 |
17
zoofy 2022-07-14 18:37:09 +08:00
@zhishixiang 你的 namespace 跟 sid 要和客户端相同的,所以在连接的时候获取到的 sid 传送给服务端,记录好,test 接口才能发信息给客户端。不能在服务端获取啊
|
18
zhishixiang OP @zoofy 意思就是说 sid 必须要客户端发给服务端,服务端无法获取吗,那服务端怎么通过客户端提供的 sid 找到客户端并向客户端发送消息
|
19
zoofy 2022-07-14 18:50:24 +08:00
@zhishixiang 不一样的,你可以打印看看。我那个代码可以直接允许的了
|
20
zhishixiang OP @zoofy 服务端第 13 行 sid 参数是从客户端获取的,但是客户端没有传 sid 参数,实际上无法运行
|
21
zhishixiang OP @zoofy 我看错了不好意思
|
22
zhishixiang OP @zoofy 确实可以用了,我测试时运行了我自己的代码导致出错,感谢大佬帮助
|
23
zhishixiang OP @zoofy 还有个问题:怎么检测客户端断开链接并移除房价以及打印日志
|
24
zoofy 2022-07-15 11:48:07 +08:00
@zhishixiang 感觉可以在注册的时候记录好 request.sid 和 request namespace sid 两个 id, 在服务端监听 disconnect 方法,获取到 request.sid 后拿到对应保存的 namespace sid, 然后再做 close room 或者 leave room 操作
|