flask socketio 无法向客户端推送消息

2022-07-13 15:24:14 +08:00
 zhishixiang

这是客户端:

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 友

2267 次点击
所在节点    Python
24 条回复
BeautifulSoup
2022-07-13 15:32:06 +08:00
请问你现在的部署环境是怎么样的?如果是开发环境,flask 自带的应用服务器是不支持 ws 协议的,需要通过前端 socketio.js 库降级为轮询使用
zhishixiang
2022-07-13 15:36:08 +08:00
@BeautifulSoup 服务端安装了 gevent-websocket 库,可以使用 websocket ,客户端用的是 python websocket 库
NessajCN
2022-07-13 16:31:53 +08:00
服务端不是这么写的
sio.connect()是客户端连服务器端的函数
服务器端要用 aiohttp 之类的库部署
https://python-socketio.readthedocs.io/en/latest/server.html#deployment-strategies
zhishixiang
2022-07-13 16:42:43 +08:00
@NessajCN 才发现服务端和客户端代码写反了,得换过来看
NessajCN
2022-07-13 17:21:26 +08:00
@zhishixiang 那你服务器端那边的 emit 之类的函数要放在 socketio 实例里啊
socketio.emit()
zhishixiang
2022-07-13 17:50:14 +08:00
@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)
```
zhishixiang
2022-07-13 17:50:45 +08:00
@zhishixiang 又忘记删机密信息了,改个密码先
NessajCN
2022-07-13 18:05:38 +08:00
@zhishixiang 我大概知道啥问题了。你客户端里是 sio.connect("ws://localhost:8090"),
但 socketio 虽然是用 websocket 实现的,你在连接的时候却不能这么写
你改成 sio.connect("http://localhost:8090") 试试
zhishixiang
2022-07-13 18:52:14 +08:00
@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)
NessajCN
2022-07-13 19:10:05 +08:00
@zhishixiang 这个报错就说明消息成功传达,只是参数读取出问题吧?譬如你传过去的{'secret':'114514'}这个参数直接 data["secret"]不行,那就 json.loads(data)再 data["secret"]
zhishixiang
2022-07-13 19:18:32 +08:00
@NessajCN 已经读取成功了,这个报错是服务端的,完全不影响使用,而且是不知道改了什么才出现的,根本修复不了,很奇怪
NessajCN
2022-07-13 19:22:33 +08:00
@zhishixiang 那不对啊,你贴的报错信息是 socketio 这个包(也就是 python-socketio ,你客户端用的就是这个)的。但你服务器端代码用的是 flask_socketio 。报错怎么会报没在用的包的错?
zhishixiang
2022-07-13 21:30:34 +08:00
@NessajCN 会不会是 flask socketio 封装了 socketio 的相关内容
raycool
2022-07-13 23:05:41 +08:00
直接用 tornado 或者 fastapi
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)进行发送
zhishixiang
2022-07-14 17:21:57 +08:00
@zoofy 还是不行,而且有几个问题:
1.sid 应该用 request.sid 获取,data 数据里面没有 sid 内容
2.目的是为了当访问 localhost:8090/test 时客户端能收到消息,但是仍未实现
3.要是实在无法解决的话有没有别的方法能实现 websocket 连接
zoofy
2022-07-14 18:37:09 +08:00
@zhishixiang 你的 namespace 跟 sid 要和客户端相同的,所以在连接的时候获取到的 sid 传送给服务端,记录好,test 接口才能发信息给客户端。不能在服务端获取啊
zhishixiang
2022-07-14 18:39:53 +08:00
@zoofy 意思就是说 sid 必须要客户端发给服务端,服务端无法获取吗,那服务端怎么通过客户端提供的 sid 找到客户端并向客户端发送消息
zoofy
2022-07-14 18:50:24 +08:00
@zhishixiang 不一样的,你可以打印看看。我那个代码可以直接允许的了
zhishixiang
2022-07-14 19:12:07 +08:00
@zoofy 服务端第 13 行 sid 参数是从客户端获取的,但是客户端没有传 sid 参数,实际上无法运行

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/865925

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX