Python中flask-SocketIO客户端使用原生WebSocket连接失败,但使用socket.io却能正常连接的原因是什么?

Python 2.7 使用 flask_socketio(3.0.1) 时,浏览器控制台下用原生

var socket = new WebSocket("ws://127.0.0.1:5000/char")

错误信息如下:

VM167:1 WebSocket connection to 'ws://127.0.0.1:5000/chat' failed: Error during WebSocket handshake: Unexpected response code: 200

引入 socket.io

socket = io.connect('ws://127.0.0.1:5000/chat')

查看 socket 的 connected 属性显示为 true

flask_socketio 的 git 地址: https://github.com/miguelgrinberg/Flask-SocketIO

是不是只能使用 socket.io 创建 client 进行连接?给的 demo 也都是使用 socket.io

求踩过坑的朋友指点一下,谢谢!


Python中flask-SocketIO客户端使用原生WebSocket连接失败,但使用socket.io却能正常连接的原因是什么?

28 回复

socket.io 用了 polling 模式吗?


这个问题核心在于Flask-SocketIO服务器默认使用Socket.IO协议,而原生WebSocket是另一个协议。

Flask-SocketIO是基于Socket.IO库实现的,它并不是纯WebSocket服务器。Socket.IO协议在WebSocket之上添加了一层自己的封装,包括:

  1. 心跳机制
  2. 自动重连
  3. 命名空间和房间
  4. 事件驱动的消息格式
  5. 回退机制(当WebSocket不可用时使用轮询)

当你用原生WebSocket客户端连接时,服务器期望收到Socket.IO格式的消息(如2probe5这样的握手包),但原生WebSocket发送的是标准WebSocket帧,协议不匹配自然连接失败。

示例对比:

# Flask-SocketIO服务器端
from flask import Flask
from flask_socketio import SocketIO

app = Flask(__name__)
socketio = SocketIO(app, cors_allowed_origins="*")

@socketio.on('message')
def handle_message(data):
    print('received message:', data)

if __name__ == '__main__':
    socketio.run(app, port=5000)
// 能连接的Socket.IO客户端
<script src="https://cdn.socket.io/4.5.0/socket.io.min.js"></script>
<script>
    const socket = io('http://localhost:5000');
    socket.emit('message', 'Hello Socket.IO');
</script>
// 会失败的原生WebSocket客户端
<script>
    const ws = new WebSocket('ws://localhost:5000');
    ws.onopen = () => {
        ws.send('Hello');  // 这里发送的是纯文本,不是Socket.IO格式
    };
</script>

解决方案:

  • 如果要用原生WebSocket,服务器端需要用纯WebSocket库(如websockets)
  • 如果要用Flask-SocketIO,客户端必须用Socket.IO客户端库

简单说:协议不同,别混着用。

你是说客户端吗?应该没有。我只是测试了下能不能连接成功。

是的,只能用 socket.io 的 client。我这边本来有个 python ws 的需求,现在就是一直往后延。。。python 做 web 可能确实不是特别合适吧。。

这就有点蛋疼了。。。 好像 tornado 没有这个问题。

F12 看下 network 里面 ws 有没有包。

client 用的是 socket.io 吗?

用 new 出来的 WebSocket 去连接会看到 WS 里面有包,但是用 socket.io 看不到。

好吧,我用 socket.io 也是没有问题的。

地址对了吗,flask socketio 就是那个作者写的 socketio 的 Py 版本,但是客户端应该不影响,只要是个 ws

看看是不是 python 少了一个依赖,貌似叫 gevent ?如果 websoket 建立不了链接,socket.io 会自动退化到 polling 模式。

咦,那有可能是,实现的不是单纯的 websocket 协议

socket io 服务器和客户端虽然可以通过 websocket 连接 但是不能用浏览器原生的 websocket 连接 socket io 服务器 socket io 有自己自定义的一套协议。

问一个问题:使用 socket.io 做服务端能直接用 websocket 当做客户端链接么?

这个是装了的,因为没装的时候会有 Warning。但是我用 flask_socketio 作者自己的 demo 跑,显示的链接也是 /socket.io/?EIO=3&transport=polling&t=1530787155994-0 这种

/char or /chat?

谢谢提醒,刚去我测试代码看了下,代码里没有错。帖子里写错了。

文档中提到 “ Unofficial clients may also work, as long as they implement the Socket.IO protocol ”. 所以只有实现 socket.io 协议的客户端可以连, 直接用 WebSocket 是不行的。

socket.io 连不着时会 fallback 到 http

其实楼上说的都不完全,socket.io 本身是一套私有协议,但是他可以基于 http polling/flash socket/websocket 等等通道进行传输,其实直接用原生的 websocket 去连接 socket.io 的服务器也不是不可以,但是实际操作的步骤就和你直接用 tcp 连接一个 http server 一样,你需要自行完成 socket.io 的握手步骤

&transport=polling 基本就是用了 polling 模式,基于 http 长轮询,并不是 ws

websocket 只是 socket.io 的其中一种工作方式。

flask-sockerio 是假的 websocket,建议用 tornado

flask-socketio 是真的 websocket 好吧

nginx 关于 websocket 的配置可以上网搜一搜,一堆。flask-socketio 的支持和前端使用的库有关,原生的是不支持的。你可以找个测试网站试试就知道了

回到顶部