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却能正常连接的原因是什么?

32 回复

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 没有这个问题。

我用 Python Flask socketio 正常。Python 版本 3.7.0

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

client 用的是 socket.io 吗?

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

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

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

两种连接方式使用的是同一个地址。我看到官方文档写了 The client-side application can use any of the SocketIO official clients libraries in Javascript, C++, Java and Swift, or any compatible client to establish a permanent connection to the server.,这算是声明了吗?

看看是不是 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 好吧

看到了这个。。。 作者自己回答了,so sad!



你好,我现在也是在使用 python3.7 和 flask-socketio,在本地跑作者的 example 没问题,
但是我部署到服务器之后,在浏览器打开控制台就一直报错 400 错误,并且每一秒都会尝试重连,
请问你们当时部署到服务器的时候,nginx 是怎么配置的呢,求解答…

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

回到顶部