You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 05:24:54