Flask与Twilio集成时WebSocket连接超时(错误31901)求助
问题:Twilio WebSocket连接超时(Error: 31901Stream - WebSocket - Connection Timeout)
部署了两个服务:transcriber.py(WebSocket服务)和app.py(Flask+Twilio),发起通话后触发流功能,但Twilio日志显示WebSocket连接超时,无法建立连接传输音频流。
现有代码文件
transcriber.py
import asyncio import websockets import json from VRD4_optimize import user_chatbot_conversation # Import your conversation function # Define the function to handle incoming WebSocket messages async def handle_message(websocket, path): try: print("Client connected") audio_file = await websocket.recv() with open("received_audio.wav", "wb") as f: f.write(audio_file) # Process the user input using your existing chatbot logic chatbot_response = await user_chatbot_conversation("received_audio.wav") output_path = "/home/ec2-user/new_rag/output.wav" # Read the processed audio file with open(output_path, "rb") as f: processed_audio_data = f.read() # Send the processed audio data back to the client await websocket.send(processed_audio_data) except Exception as e: # If an error occurs, send an error response back to the client error_response = { "type": "error", "content": f"An error occurred: {str(e)}" } await websocket.send(json.dumps(error_response)) if __name__ == "__main__": server = websockets.serve(handle_message, "0.0.0.0", 8768) # Listen on all interfaces print("WebSocket server started.") asyncio.get_event_loop().run_until_complete(server) asyncio.get_event_loop().run_forever()
app.py
client = Client(account_sid, auth_token) @app.route('/make_call', methods=['POST']) def make_call(): try: # Make a call using Twilio call = client.calls.create( url="http://server-ip:5000", to=to_number, from_=from_number ) print("Call SID:", call.sid) return 'Call initiated successfully', 200 except Exception as e: print("Exception:", e) return 'Error initiating call', 500 @app.route("/", methods=['POST']) def twilio_webhook(): if request.method == 'POST': xml = f""" <Response> <Connect> <Stream url="wss://server-ip:8768/" /> </Connect> </Response> """ return Response(xml, mimetype='text/xml') else: return f"Real-time phone call transcription app" if __name__ == "__main__": # Start Flask app app.run(host='0.0.0.0', port=5000)
排查与解决步骤
1. 网络与端口验证
- 确认服务器8768端口已在防火墙/安全组中对外开放,Twilio需要能访问该端口。可以用
wscat -c wss://server-ip:8768工具测试连通性,若无法连接则说明端口未开放或服务未正常启动。 - 检查服务器是否有NAT/反向代理配置,确保WebSocket请求能正确转发到8768端口。
2. 添加WebSocket TLS支持
Twilio要求WebSocket流使用wss加密协议,当前服务仅支持未加密的ws,这是连接超时的核心原因之一:
- 为WebSocket服务配置TLS证书(推荐用Let's Encrypt免费证书),修改
transcriber.py启动代码:
import ssl ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) # 替换为你的证书和密钥路径 ssl_context.load_cert_chain('/path/to/fullchain.pem', '/path/to/privkey.pem') if __name__ == "__main__": server = websockets.serve(handle_message, "0.0.0.0", 8768, ssl=ssl_context) print("WebSocket server started with TLS.") asyncio.get_event_loop().run_until_complete(server) asyncio.get_event_loop().run_forever()
- 注意:若用IP而非域名,自签名证书可能不被Twilio接受,建议绑定域名并使用正规证书。
3. 适配Twilio的WebSocket消息格式
Twilio发送的是实时音频帧(JSON格式封装的PCMU编码片段),而非完整WAV文件,原代码逻辑错误会导致阻塞超时:
- 恢复并修改消息循环逻辑,处理Twilio的标准流消息:
async def handle_message(websocket, path): try: print("Client connected") # 必须保留循环接收实时消息 async for message in websocket: data = json.loads(message) if data['event'] == 'start': print("Stream started") elif data['event'] == 'media': # 获取base64编码的PCMU音频帧 audio_payload = data['media']['payload'] # 在此处处理实时音频片段(如转码、拼接或传入对话函数) elif data['event'] == 'stop': print("Stream stopped") # 处理流结束后的逻辑 except Exception as e: print(f"Error: {str(e)}") error_response = { "type": "error", "content": f"An error occurred: {str(e)}" } await websocket.send(json.dumps(error_response))
4. 优化服务运行状态
- 确认
transcriber.py服务在后台稳定运行,查看日志排查启动报错。 - 检查服务器CPU/内存资源,确保音频处理函数
user_chatbot_conversation不会因耗时过长导致连接超时。
5. Flask服务的HTTPS配置(可选)
Twilio默认要求Webhook使用HTTPS,若当前用HTTP,需在Twilio控制台临时允许HTTP(不推荐生产环境),或为Flask添加HTTPS支持:
- 用
flask run --ssl-cert=fullchain.pem --ssl-key=privkey.pem启动Flask,或通过Nginx反向代理配置HTTPS。
内容的提问来源于stack exchange,提问作者vinoth kumar
相关产品推荐
相关产品推荐

