背景:
原本的项目使用Flask和Socket.IO进行开发,现在需要将Flask转换为FastAPI。在转换过程中遇到了一些问题。
问题现象:
在Python中的客户端无法与服务端建立连接。客户端代码看起来没有明显的问题,命名空间和服务端的地址看起来都没问题,并且在原来的Flask中是正常使用的。但是转换之后似乎连接不上。因为服务端没有接收到客户端发送的消息。服务端代码如下:
app = FastAPI()
logging.basicConfig(level=logging.INFO)
#设置允许的跨域源
origins = [
"http://localhost:8080", # Vue.js 前端地址
"http://127.0.0.1:8080", # Vue.js 前端地址
]
# 添加中间件
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 其他代码
io = socketio.AsyncServer(async_mode='asgi',cors_allowed_origins=[],namespaces=['/'+token])
handle_websocket_events(io,token)
def handle_websocket_events(sio,namespace):
#断开连接事件
@sio.on('disconnect', namespace='/'+namespace)
async def handle_disconnect():
# 断开连接自动关闭子进程
if namespace in user_process:
user_process[namespace].terminate()
#接收网页端选择数据,并发送给子进程
@sio.on('updateUIState', namespace='/'+namespace)
async def handle_ui_state(data):
print('\n用户 '+namespace+' 的连接:\n接收到网页端选择的数据 '+data+'\n以上数据将要发送给子进程 \n')
sio.emit('ui_state', data)
#接收网页端输入数据,并发送给子进程
@sio.on('updateUIInput', namespace='/'+namespace)
async def handle_ui_input(data):
print('\n用户 '+namespace+' 的连接:\n接收到网页端输入的数据 '+data+'\n以上数据将要发送给子进程 \n')
sio.emit('input_text', data)
#接收子进程输入数据,并发送给网页端
@sio.on('childInputData', namespace='/'+namespace)
async def get_child_data(data):
print('\n用户 '+namespace+' 的连接:\n接收到子进程返回输入的数据 '+json.dumps(data)+'\n以上数据将要发送给网页端 \n')
sio.emit('inputResponse', data)
# 接收子进程选项数据,并发送给网页端
@sio.on('childData', namespace='/'+namespace)
async def get_child_data(data):
print('\n用户 '+namespace+' 的连接:\n接收到子进程返回选择的数据 '+json.dumps(data)+'\n以上数据将要发送给网页端')
sio.emit('uiResponse', data)
# sio.attach(app,socketio_path='/game/socket.io')
app.mount('/', socketio.ASGIApp(socketio_server=sio))
在项目中还有一个网页客户端,而在FastAPI中,Socket.IO服务似乎默认支持跨域(不明白原理)。如果不将cors_allowed_origins设置为空列表[],则在网页端的响应头中会存在重复的Access-Control-Allow-Origin和Access-Control-Allow-Credentials。
只有将cors_allowed_origins设置为空列表[],网页客户端才可以正常连接上Socket。会不会是因为cors_allowed_origins为空列表[],导致Python的客户端无法连接?
python客户端socket相关的代码如下:
def __init__(self,options,context, userName=''):
self.userName = userName
self.sio = socketio.AsyncClient()
self.sio.connect('http://127.0.0.1:5000',namespaces=['/'+userName])
self.sioEvent()
def sioEvent(self):
@self.sio.on('ui_state',namespace='/'+self.userName)
async def ui_state(state):
#print('接收选择数据')
@self.sio.on('input_text',namespace='/'+self.userName)
async def input_text(text):
#print('接收输入数据')
def run(self):
format = self.changeFormat(self.options,self.context)
#print('用户'+self.userName+'子进程发送选项数据')
# 向主进程发送数据
self.sio.emit('childData', format,namespace='/'+self.userName)
while True:
if self.state:
break
return self.state
由于不熟悉异步以及FastAPI,不确定是不是关于异步的问题,看了官方文档以及各种搜索都找不到解决方法,被这个问题卡了两天,实在没办法了o(╥﹏╥)o