274 lines
8.6 KiB
Python
274 lines
8.6 KiB
Python
|
#!/usr/bin/env python3
|
|||
|
# -*- coding: utf-8 -*-
|
|||
|
"""
|
|||
|
最小功能测试框架 - Flask + SocketIO + threading
|
|||
|
用于验证打包exe后HTTP和WebSocket服务的可用性
|
|||
|
"""
|
|||
|
|
|||
|
import os
|
|||
|
import sys
|
|||
|
from flask import Flask, render_template_string, jsonify
|
|||
|
from flask_socketio import SocketIO, emit
|
|||
|
import logging
|
|||
|
from datetime import datetime
|
|||
|
|
|||
|
# 配置日志
|
|||
|
logging.basicConfig(
|
|||
|
level=logging.INFO,
|
|||
|
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|||
|
)
|
|||
|
logger = logging.getLogger(__name__)
|
|||
|
|
|||
|
# 创建Flask应用
|
|||
|
app = Flask(__name__)
|
|||
|
app.config['SECRET_KEY'] = 'minimal-test-secret-key-2024'
|
|||
|
|
|||
|
# 初始化SocketIO,强制使用threading模式
|
|||
|
try:
|
|||
|
logger.info("初始化SocketIO(threading模式)...")
|
|||
|
socketio = SocketIO(
|
|||
|
app,
|
|||
|
cors_allowed_origins='*',
|
|||
|
async_mode='threading',
|
|||
|
logger=False,
|
|||
|
engineio_logger=False
|
|||
|
)
|
|||
|
logger.info(f"SocketIO初始化成功,异步模式: {socketio.async_mode}")
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"SocketIO初始化失败: {e}")
|
|||
|
sys.exit(1)
|
|||
|
|
|||
|
# HTML测试页面模板
|
|||
|
HTML_TEMPLATE = """
|
|||
|
<!DOCTYPE html>
|
|||
|
<html>
|
|||
|
<head>
|
|||
|
<title>最小功能测试 - Flask + SocketIO</title>
|
|||
|
<meta charset="utf-8">
|
|||
|
<style>
|
|||
|
body { font-family: Arial, sans-serif; margin: 40px; }
|
|||
|
.container { max-width: 800px; margin: 0 auto; }
|
|||
|
.section { margin: 20px 0; padding: 20px; border: 1px solid #ddd; border-radius: 5px; }
|
|||
|
.success { color: green; }
|
|||
|
.error { color: red; }
|
|||
|
.info { color: blue; }
|
|||
|
button { padding: 10px 20px; margin: 5px; cursor: pointer; }
|
|||
|
#messages { height: 200px; overflow-y: auto; border: 1px solid #ccc; padding: 10px; background: #f9f9f9; }
|
|||
|
.message { margin: 5px 0; }
|
|||
|
</style>
|
|||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<div class="container">
|
|||
|
<h1>最小功能测试框架</h1>
|
|||
|
<p class="info">测试Flask HTTP服务和SocketIO WebSocket服务</p>
|
|||
|
|
|||
|
<!-- HTTP API测试 -->
|
|||
|
<div class="section">
|
|||
|
<h2>HTTP API 测试</h2>
|
|||
|
<button onclick="testHttpApi()">测试 HTTP API</button>
|
|||
|
<div id="http-result"></div>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- WebSocket测试 -->
|
|||
|
<div class="section">
|
|||
|
<h2>WebSocket 测试</h2>
|
|||
|
<button onclick="connectSocket()">连接 WebSocket</button>
|
|||
|
<button onclick="disconnectSocket()">断开连接</button>
|
|||
|
<button onclick="sendTestMessage()">发送测试消息</button>
|
|||
|
<div>连接状态: <span id="connection-status" class="error">未连接</span></div>
|
|||
|
<div id="messages"></div>
|
|||
|
</div>
|
|||
|
|
|||
|
<!-- 系统信息 -->
|
|||
|
<div class="section">
|
|||
|
<h2>系统信息</h2>
|
|||
|
<div id="system-info">
|
|||
|
<p>服务器时间: {{ server_time }}</p>
|
|||
|
<p>SocketIO模式: {{ socketio_mode }}</p>
|
|||
|
<p>Flask版本: {{ flask_version }}</p>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
|
|||
|
<script>
|
|||
|
let socket = null;
|
|||
|
|
|||
|
function addMessage(message, type = 'info') {
|
|||
|
const messages = document.getElementById('messages');
|
|||
|
const div = document.createElement('div');
|
|||
|
div.className = `message ${type}`;
|
|||
|
div.innerHTML = `[${new Date().toLocaleTimeString()}] ${message}`;
|
|||
|
messages.appendChild(div);
|
|||
|
messages.scrollTop = messages.scrollHeight;
|
|||
|
}
|
|||
|
|
|||
|
function testHttpApi() {
|
|||
|
const resultDiv = document.getElementById('http-result');
|
|||
|
resultDiv.innerHTML = '测试中...';
|
|||
|
|
|||
|
fetch('/api/test')
|
|||
|
.then(response => response.json())
|
|||
|
.then(data => {
|
|||
|
resultDiv.innerHTML = `<span class="success">✓ HTTP API 正常</span><br>响应: ${JSON.stringify(data, null, 2)}`;
|
|||
|
})
|
|||
|
.catch(error => {
|
|||
|
resultDiv.innerHTML = `<span class="error">✗ HTTP API 错误: ${error}</span>`;
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
function connectSocket() {
|
|||
|
if (socket && socket.connected) {
|
|||
|
addMessage('WebSocket已连接', 'info');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
socket = io();
|
|||
|
|
|||
|
socket.on('connect', function() {
|
|||
|
document.getElementById('connection-status').innerHTML = '<span class="success">已连接</span>';
|
|||
|
addMessage('WebSocket连接成功', 'success');
|
|||
|
});
|
|||
|
|
|||
|
socket.on('disconnect', function() {
|
|||
|
document.getElementById('connection-status').innerHTML = '<span class="error">未连接</span>';
|
|||
|
addMessage('WebSocket连接断开', 'error');
|
|||
|
});
|
|||
|
|
|||
|
socket.on('test_response', function(data) {
|
|||
|
addMessage(`收到服务器响应: ${JSON.stringify(data)}`, 'success');
|
|||
|
});
|
|||
|
|
|||
|
socket.on('server_message', function(data) {
|
|||
|
addMessage(`服务器消息: ${data.message}`, 'info');
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
function disconnectSocket() {
|
|||
|
if (socket) {
|
|||
|
socket.disconnect();
|
|||
|
socket = null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function sendTestMessage() {
|
|||
|
if (!socket || !socket.connected) {
|
|||
|
addMessage('请先连接WebSocket', 'error');
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
const testData = {
|
|||
|
message: 'Hello from client',
|
|||
|
timestamp: new Date().toISOString()
|
|||
|
};
|
|||
|
|
|||
|
socket.emit('test_message', testData);
|
|||
|
addMessage(`发送消息: ${JSON.stringify(testData)}`, 'info');
|
|||
|
}
|
|||
|
|
|||
|
// 页面加载时自动连接
|
|||
|
window.onload = function() {
|
|||
|
connectSocket();
|
|||
|
};
|
|||
|
</script>
|
|||
|
</body>
|
|||
|
</html>
|
|||
|
"""
|
|||
|
|
|||
|
# HTTP路由
|
|||
|
@app.route('/')
|
|||
|
def index():
|
|||
|
"""主页面"""
|
|||
|
import flask
|
|||
|
return render_template_string(HTML_TEMPLATE,
|
|||
|
server_time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
|||
|
socketio_mode=socketio.async_mode,
|
|||
|
flask_version=flask.__version__
|
|||
|
)
|
|||
|
|
|||
|
@app.route('/api/test')
|
|||
|
def api_test():
|
|||
|
"""HTTP API测试接口"""
|
|||
|
return jsonify({
|
|||
|
'status': 'success',
|
|||
|
'message': 'HTTP API工作正常',
|
|||
|
'timestamp': datetime.now().isoformat(),
|
|||
|
'server_info': {
|
|||
|
'socketio_mode': socketio.async_mode,
|
|||
|
'working_directory': os.getcwd()
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
@app.route('/health')
|
|||
|
def health_check():
|
|||
|
"""健康检查接口"""
|
|||
|
return jsonify({
|
|||
|
'status': 'healthy',
|
|||
|
'services': {
|
|||
|
'http': 'running',
|
|||
|
'websocket': 'running',
|
|||
|
'socketio_mode': socketio.async_mode
|
|||
|
},
|
|||
|
'timestamp': datetime.now().isoformat()
|
|||
|
})
|
|||
|
|
|||
|
# SocketIO事件处理
|
|||
|
@socketio.on('connect')
|
|||
|
def handle_connect():
|
|||
|
"""客户端连接事件"""
|
|||
|
logger.info(f"客户端连接: {request.sid if 'request' in globals() else 'unknown'}")
|
|||
|
emit('server_message', {
|
|||
|
'message': 'WebSocket连接成功',
|
|||
|
'timestamp': datetime.now().isoformat(),
|
|||
|
'socketio_mode': socketio.async_mode
|
|||
|
})
|
|||
|
|
|||
|
@socketio.on('disconnect')
|
|||
|
def handle_disconnect():
|
|||
|
"""客户端断开连接事件"""
|
|||
|
logger.info(f"客户端断开连接: {request.sid if 'request' in globals() else 'unknown'}")
|
|||
|
|
|||
|
@socketio.on('test_message')
|
|||
|
def handle_test_message(data):
|
|||
|
"""处理测试消息"""
|
|||
|
logger.info(f"收到测试消息: {data}")
|
|||
|
|
|||
|
# 回复客户端
|
|||
|
emit('test_response', {
|
|||
|
'status': 'received',
|
|||
|
'original_message': data,
|
|||
|
'server_timestamp': datetime.now().isoformat(),
|
|||
|
'socketio_mode': socketio.async_mode
|
|||
|
})
|
|||
|
|
|||
|
def main():
|
|||
|
"""主函数"""
|
|||
|
logger.info("="*50)
|
|||
|
logger.info("最小功能测试框架启动")
|
|||
|
logger.info("="*50)
|
|||
|
logger.info(f"工作目录: {os.getcwd()}")
|
|||
|
logger.info(f"Python版本: {sys.version}")
|
|||
|
logger.info(f"SocketIO异步模式: {socketio.async_mode}")
|
|||
|
|
|||
|
try:
|
|||
|
# 启动服务器
|
|||
|
logger.info("启动服务器 http://localhost:5000")
|
|||
|
logger.info("按 Ctrl+C 停止服务器")
|
|||
|
|
|||
|
socketio.run(
|
|||
|
app,
|
|||
|
host='0.0.0.0',
|
|||
|
port=5000,
|
|||
|
debug=False,
|
|||
|
allow_unsafe_werkzeug=True
|
|||
|
)
|
|||
|
|
|||
|
except KeyboardInterrupt:
|
|||
|
logger.info("用户中断,正在关闭服务器...")
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"服务器启动失败: {e}")
|
|||
|
input("按回车键退出...")
|
|||
|
sys.exit(1)
|
|||
|
|
|||
|
if __name__ == '__main__':
|
|||
|
main()
|