flask socketio 无法向客户端推送消息 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
zhishixiang
V2EX    Python

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

  •  
  •   zhishixiang
    zhishixiang 2022-07-13 15:24:14 +08:00 3070 次点击
    这是一个创建于 1256 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这是客户端:

    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 友

    24 条回复    2022-07-15 11:48:07 +08:00
    BeautifulSoup
        1
    BeautifulSoup  
       2022-07-13 15:32:06 +08:00
    请问你现在的部署环境是怎么样的?如果是开发环境,flask 自带的应用服务器是不支持 ws 协议的,需要通过前端 socketio.js 库降级为轮询使用
    zhishixiang
        2
    zhishixiang  
    OP
       2022-07-13 15:36:08 +08:00
    @BeautifulSoup 服务端安装了 gevent-websocket 库,可以使用 websocket ,客户端用的是 python websocket 库
    NessajCN
        3
    NessajCN  
       2022-07-13 16:31:53 +08:00
    服务端不是这么写的
    sio.connect()是客户端连服务器端的函数
    服务器端要用 aiohttp 之类的库部署
    https://python-socketio.readthedocs.io/en/latest/server.html#deployment-strategies
    zhishixiang
        4
    zhishixiang  
    OP
       2022-07-13 16:42:43 +08:00
    @NessajCN 才发现服务端和客户端代码写反了,得换过来看
    NessajCN
        5
    NessajCN  
       2022-07-13 17:21:26 +08:00
    @zhishixiang 那你服务器端那边的 emit 之类的函数要放在 socketio 实例里啊
    socketio.emit()
    zhishixiang
        6
    zhishixiang  
    OP
       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
        7
    zhishixiang  
    OP
       2022-07-13 17:50:45 +08:00
    @zhishixiang 又忘记删机密信息了,改个密码先
    NessajCN
        8
    NessajCN  
       2022-07-13 18:05:38 +08:00
    @zhishixiang 我大概知道啥问题了。你客户端里是 sio.connect("ws://localhost:8090"),
    但 socketio 虽然是用 websocket 实现的,你在连接的时候却不能这么写
    你改成 sio.connect("http://localhost:8090") 试试
    zhishixiang
        9
    zhishixiang  
    OP
       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
        10
    NessajCN  
       2022-07-13 19:10:05 +08:00
    @zhishixiang 这个报错就说明消息成功传达,只是参数读取出问题吧?譬如你传过去的{'secret':'114514'}这个参数直接 data["secret"]不行,那就 json.loads(data)再 data["secret"]
    zhishixiang
        11
    zhishixiang  
    OP
       2022-07-13 19:18:32 +08:00
    @NessajCN 已经读取成功了,这个报错是服务端的,完全不影响使用,而且是不知道改了什么才出现的,根本修复不了,很奇怪
    NessajCN
        12
    NessajCN  
       2022-07-13 19:22:33 +08:00
    @zhishixiang 那不对啊,你贴的报错信息是 socketio 这个包(也就是 python-socketio ,你客户端用的就是这个)的。但你服务器端代码用的是 flask_socketio 。报错怎么会报没在用的包的错?
    zhishixiang
        13
    zhishixiang  
    OP
       2022-07-13 21:30:34 +08:00 via Android
    @NessajCN 会不会是 flask socketio 封装了 socketio 的相关内容
    raycool
        14
    raycool  
       2022-07-13 23:05:41 +08:00
    直接用 tornado 或者 fastapi
    zoofy
        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)进行发送
    zhishixiang
        16
    zhishixiang  
    OP
       2022-07-14 17:21:57 +08:00
    @zoofy 还是不行,而且有几个问题:
    1.sid 应该用 request.sid 获取,data 数据里面没有 sid 内容
    2.目的是为了当访问 localhost:8090/test 时客户端能收到消息,但是仍未实现
    3.要是实在无法解决的话有没有别的方法能实现 websocket 连接
    zoofy
        17
    zoofy  
       2022-07-14 18:37:09 +08:00
    @zhishixiang 你的 namespace 跟 sid 要和客户端相同的,所以在连接的时候获取到的 sid 传送给服务端,记录好,test 接口才能发信息给客户端。不能在服务端获取啊
    zhishixiang
        18
    zhishixiang  
    OP
       2022-07-14 18:39:53 +08:00
    @zoofy 意思就是说 sid 必须要客户端发给服务端,服务端无法获取吗,那服务端怎么通过客户端提供的 sid 找到客户端并向客户端发送消息
    zoofy
        19
    zoofy  
       2022-07-14 18:50:24 +08:00
    @zhishixiang 不一样的,你可以打印看看。我那个代码可以直接允许的了
    zhishixiang
        20
    zhishixiang  
    OP
       2022-07-14 19:12:07 +08:00
    @zoofy 服务端第 13 行 sid 参数是从客户端获取的,但是客户端没有传 sid 参数,实际上无法运行
    zhishixiang
        21
    zhishixiang  
    OP
       2022-07-14 19:12:36 +08:00
    @zoofy 我看错了不好意思
    zhishixiang
        22
    zhishixiang  
    OP
       2022-07-14 19:14:21 +08:00
    @zoofy 确实可以用了,我测试时运行了我自己的代码导致出错,感谢大佬帮助
    zhishixiang
        23
    zhishixiang  
    OP
       2022-07-14 21:21:31 +08:00
    @zoofy 还有个问题:怎么检测客户端断开链接并移除房价以及打印日志
    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 操作
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1612 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 16:15 PVG 00:15 LAX 08:15 JFK 11:15
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86