dashboard/backend/network_diagnostic.py
2025-06-09 14:59:40 +08:00

304 lines
9.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
网络诊断工具
用于诊断服务器部署后访问外部网站时的网络连接问题
"""
import socket
import requests
import time
import urllib.parse
import subprocess
import platform
from typing import Dict, List
def diagnose_network_connectivity(url: str) -> Dict:
"""
综合诊断网络连接问题
"""
print(f"开始诊断网络连接: {url}")
results = {
'url': url,
'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'),
'tests': {}
}
# 解析URL
try:
parsed_url = urllib.parse.urlparse(url)
hostname = parsed_url.hostname
port = parsed_url.port or (443 if parsed_url.scheme == 'https' else 80)
results['hostname'] = hostname
results['port'] = port
except Exception as e:
results['error'] = f"URL解析失败: {e}"
return results
# 1. DNS解析测试
results['tests']['dns_resolution'] = test_dns_resolution(hostname)
# 2. TCP连接测试
results['tests']['tcp_connection'] = test_tcp_connection(hostname, port)
# 3. HTTP请求测试
results['tests']['http_request'] = test_http_request(url)
# 4. 系统网络配置检查
results['tests']['system_info'] = get_system_network_info()
# 5. 建议解决方案
results['suggestions'] = generate_suggestions(results['tests'])
return results
def test_dns_resolution(hostname: str) -> Dict:
"""测试DNS解析"""
print(f"测试DNS解析: {hostname}")
test_result = {
'status': 'unknown',
'details': {}
}
try:
start_time = time.time()
ip_address = socket.gethostbyname(hostname)
dns_time = time.time() - start_time
test_result['status'] = 'success'
test_result['details'] = {
'ip_address': ip_address,
'resolution_time': f"{dns_time:.3f}"
}
print(f"DNS解析成功: {hostname} -> {ip_address} ({dns_time:.3f}秒)")
except socket.gaierror as e:
test_result['status'] = 'failed'
test_result['details'] = {
'error': str(e),
'error_code': e.errno if hasattr(e, 'errno') else 'unknown'
}
print(f"DNS解析失败: {e}")
return test_result
def test_tcp_connection(hostname: str, port: int) -> Dict:
"""测试TCP连接"""
print(f"测试TCP连接: {hostname}:{port}")
test_result = {
'status': 'unknown',
'details': {}
}
try:
start_time = time.time()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10) # 10秒超时
result = sock.connect_ex((hostname, port))
connect_time = time.time() - start_time
sock.close()
if result == 0:
test_result['status'] = 'success'
test_result['details'] = {
'connection_time': f"{connect_time:.3f}",
'port_open': True
}
print(f"TCP连接成功: {hostname}:{port} ({connect_time:.3f}秒)")
else:
test_result['status'] = 'failed'
test_result['details'] = {
'connection_time': f"{connect_time:.3f}",
'port_open': False,
'error_code': result
}
print(f"TCP连接失败: {hostname}:{port} (错误代码: {result})")
except Exception as e:
test_result['status'] = 'failed'
test_result['details'] = {
'error': str(e)
}
print(f"TCP连接测试异常: {e}")
return test_result
def test_http_request(url: str) -> Dict:
"""测试HTTP请求"""
print(f"测试HTTP请求: {url}")
test_result = {
'status': 'unknown',
'details': {}
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
try:
start_time = time.time()
response = requests.get(
url,
headers=headers,
timeout=(10, 30), # (连接超时, 读取超时)
verify=False,
allow_redirects=True
)
request_time = time.time() - start_time
test_result['status'] = 'success'
test_result['details'] = {
'status_code': response.status_code,
'request_time': f"{request_time:.3f}",
'content_length': len(response.content),
'encoding': response.encoding,
'headers_count': len(response.headers)
}
print(f"HTTP请求成功: {response.status_code} ({request_time:.3f}秒)")
except requests.exceptions.Timeout as e:
test_result['status'] = 'timeout'
test_result['details'] = {
'error': '请求超时',
'timeout_type': str(type(e).__name__)
}
print(f"HTTP请求超时: {e}")
except requests.exceptions.ConnectionError as e:
test_result['status'] = 'connection_error'
test_result['details'] = {
'error': '连接错误',
'error_detail': str(e)
}
print(f"HTTP连接错误: {e}")
except Exception as e:
test_result['status'] = 'failed'
test_result['details'] = {
'error': str(e)
}
print(f"HTTP请求失败: {e}")
return test_result
def get_system_network_info() -> Dict:
"""获取系统网络信息"""
print("收集系统网络信息...")
info = {
'platform': platform.system(),
'platform_version': platform.version(),
'python_version': platform.python_version()
}
try:
# 获取本机IP地址
hostname = socket.gethostname()
local_ip = socket.gethostbyname(hostname)
info['hostname'] = hostname
info['local_ip'] = local_ip
except:
info['hostname'] = 'unknown'
info['local_ip'] = 'unknown'
# 检查网络配置仅在Linux/Unix系统上
if platform.system() in ['Linux', 'Darwin']:
try:
# 检查DNS服务器
with open('/etc/resolv.conf', 'r') as f:
dns_servers = []
for line in f:
if line.startswith('nameserver'):
dns_servers.append(line.split()[1])
info['dns_servers'] = dns_servers
except:
info['dns_servers'] = 'unavailable'
try:
# 检查默认网关
result = subprocess.run(['ip', 'route', 'show', 'default'],
capture_output=True, text=True, timeout=5)
if result.returncode == 0:
info['default_gateway'] = result.stdout.strip()
else:
info['default_gateway'] = 'unavailable'
except:
info['default_gateway'] = 'unavailable'
return info
def generate_suggestions(test_results: Dict) -> List[str]:
"""根据测试结果生成建议"""
suggestions = []
# DNS问题
if test_results.get('dns_resolution', {}).get('status') == 'failed':
suggestions.extend([
"DNS解析失败请检查服务器的DNS配置",
"尝试使用公共DNS服务器 (8.8.8.8, 114.114.114.114)",
"检查 /etc/resolv.conf 文件中的DNS服务器配置"
])
# TCP连接问题
if test_results.get('tcp_connection', {}).get('status') == 'failed':
suggestions.extend([
"TCP连接失败可能是防火墙阻止了连接",
"检查服务器的出站规则设置",
"确认目标端口是否开放"
])
# HTTP请求问题
http_status = test_results.get('http_request', {}).get('status')
if http_status == 'timeout':
suggestions.extend([
"HTTP请求超时网络延迟较高",
"增加超时时间设置",
"考虑使用重试机制"
])
elif http_status == 'connection_error':
suggestions.extend([
"HTTP连接错误可能是网络配置问题",
"检查代理设置",
"确认SSL/TLS证书配置"
])
# 通用建议
if not suggestions:
suggestions.append("网络连接测试基本正常,问题可能在应用层面")
suggestions.extend([
"联系系统管理员检查网络配置",
"考虑使用VPN或代理服务器",
"检查服务器所在云平台的安全组设置"
])
return suggestions
def main():
"""主函数,用于命令行测试"""
test_url = "https://ac.bit.edu.cn"
print("=" * 60)
print("网络连接诊断工具")
print("=" * 60)
results = diagnose_network_connectivity(test_url)
print("\n" + "=" * 60)
print("诊断结果")
print("=" * 60)
for test_name, test_result in results['tests'].items():
print(f"\n{test_name}: {test_result['status']}")
if 'details' in test_result:
for key, value in test_result['details'].items():
print(f" {key}: {value}")
print("\n" + "=" * 60)
print("建议解决方案")
print("=" * 60)
for i, suggestion in enumerate(results['suggestions'], 1):
print(f"{i}. {suggestion}")
if __name__ == "__main__":
main()