
这是客户端:
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 库 |
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 给你贴个可以运行的代码 ``` 服务端 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 操作 |