基于Python的Web版网络连通性测试工具

# 文件名: ping.py
# 作者: wanghongwei
# 日期: 2025年7月16日
# 版本: 1.0
# 描述: 基于Python的Web版网络连通性测试工具,通过浏览器界面执行Ping命令并展示结果
# 使用方式: 直接运行脚本,然后在浏览器访问 http://localhost:8000,输入目标域名或IP地址进行Ping测试

import http.server
import socketserver
import subprocess
import urllib.parse
import re
import sys
import locale
import platform

PORT = 8000

class PingHandler(http.server.BaseHTTPRequestHandler):
    def _set_headers(self, status=200):
        self.send_response(status)
        self.send_header('Content-type', 'text/html; charset=utf-8')
        self.end_headers()
    
    def _get_system_encoding(self):
        """获取系统控制台编码"""
        try:
            # Windows系统通常使用GBK编码
            if platform.system() == 'Windows':
                return 'gbk'
            # Linux/Mac系统使用UTF-8
            return 'utf-8'
        except:
            return 'utf-8'

    def do_GET(self):
        if self.path == '/':
            self._set_headers()
            
            # 返回包含ping表单的HTML页面
            html = """
            <!DOCTYPE html>
            <html>
            <head>
                <meta charset="UTF-8">
                <title>网络连通性测试</title>
                <style>
                    body { font-family: "Microsoft YaHei", "Segoe UI", sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background-color: #f0f8ff; }
                    .container { background-color: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 15px rgba(0,0,0,0.1); }
                    h1 { color: #2c3e50; text-align: center; margin-bottom: 25px; }
                    form { display: flex; margin: 25px 0; }
                    input[type="text"] { flex-grow: 1; padding: 12px 15px; font-size: 16px; border: 2px solid #3498db; border-radius: 4px; transition: border-color 0.3s; }
                    input[type="text"]:focus { border-color: #2980b9; outline: none; }
                    button { background-color: #3498db; color: white; border: none; padding: 12px 24px; margin-left: 12px; border-radius: 4px; cursor: pointer; font-size: 16px; font-weight: bold; transition: background-color 0.3s; }
                    button:hover { background-color: #2980b9; }
                    .result { background-color: #f8f9fa; padding: 20px; border-radius: 4px; margin-top: 25px; white-space: pre-wrap; font-family: Consolas, Monaco, monospace; font-size: 14px; border: 1px solid #e0e0e0; overflow: auto; max-height: 400px; }
                    .error { color: #d32f2f; background-color: #ffebee; border-color: #ffcdd2; }
                    footer { text-align: center; margin-top: 30px; color: #7f8c8d; font-size: 14px; }
                </style>
            </head>
            <body>
                <div class="container">
                    <h1>网络连通性测试工具</h1>
                    <form method="POST">
                        <input type="text" name="target" placeholder="输入要ping的域名或IP地址" required>
                        <button type="submit">执行Ping测试</button>
                    </form>
                    <div class="result">请输入目标地址并点击测试按钮</div>
                    <footer>本工具仅用于网络诊断 | 使用Python构建</footer>
                </div>
            </body>
            </html>
            """
            self.wfile.write(html.encode('utf-8'))
        else:
            self.send_error(404, "页面不存在")

    def do_POST(self):
        content_length = int(self.headers['Content-Length'])
        post_data = self.rfile.read(content_length).decode('utf-8')
        params = urllib.parse.parse_qs(post_data)
        
        target = params.get('target', [''])[0].strip()
        
        # 安全过滤:只允许字母、数字、点、连字符和冒号(用于IPv6)
        if not re.match(r'^[a-zA-Z0-9\.\-:]+$', target):
            result = "错误:输入包含非法字符"
            result_class = "error"
        else:
            try:
                # 根据操作系统确定ping命令参数
                count_param = '-n' if platform.system() == 'Windows' else '-c'
                
                # 执行ping命令(不使用text=True参数,手动处理编码)
                process = subprocess.Popen(
                    ['ping', count_param, '4', target],
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE
                )
                
                # 获取系统编码
                encoding = self._get_system_encoding()
                
                try:
                    # 设置超时并获取输出
                    output, error = process.communicate(timeout=10)
                    
                    # 尝试使用系统编码解码
                    try:
                        result = output.decode(encoding) if output else error.decode(encoding)
                    except UnicodeDecodeError:
                        # 如果系统编码失败,尝试UTF-8和GBK
                        try:
                            result = output.decode('utf-8') if output else error.decode('utf-8')
                        except:
                            result = output.decode('gbk', errors='replace') if output else error.decode('gbk', errors='replace')
                
                except subprocess.TimeoutExpired:
                    process.kill()
                    output, error = process.communicate()
                    result = "错误:ping操作超时"
                
                result_class = ""
                
            except Exception as e:
                result = f"错误:{str(e)}"
                result_class = "error"
        
        self._set_headers()
        
        # 安全转义结果中的HTML特殊字符
        result = result.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;')
        
        # 返回包含结果的HTML页面
        result_html = f"""
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <title>网络连通性测试</title>
            <style>
                body {{ font-family: "Microsoft YaHei", "Segoe UI", sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background-color: #f0f8ff; }}
                .container {{ background-color: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 15px rgba(0,0,0,0.1); }}
                h1 {{ color: #2c3e50; text-align: center; margin-bottom: 25px; }}
                form {{ display: flex; margin: 25px 0; }}
                input[type="text"] {{ flex-grow: 1; padding: 12px 15px; font-size: 16px; border: 2px solid #3498db; border-radius: 4px; transition: border-color 0.3s; }}
                input[type="text"]:focus {{ border-color: #2980b9; outline: none; }}
                button {{ background-color: #3498db; color: white; border: none; padding: 12px 24px; margin-left: 12px; border-radius: 4px; cursor: pointer; font-size: 16px; font-weight: bold; transition: background-color 0.3s; }}
                button:hover {{ background-color: #2980b9; }}
                .result {{ background-color: #f8f9fa; padding: 20px; border-radius: 4px; margin-top: 25px; white-space: pre-wrap; font-family: Consolas, Monaco, monospace; font-size: 14px; border: 1px solid #e0e0e0; overflow: auto; max-height: 400px; }}
                .error {{ color: #d32f2f; background-color: #ffebee; border-color: #ffcdd2; }}
                footer {{ text-align: center; margin-top: 30px; color: #7f8c8d; font-size: 14px; }}
            </style>
        </head>
        <body>
            <div class="container">
                <h1>网络连通性测试工具</h1>
                <form method="POST">
                    <input type="text" name="target" value="{target}" required>
                    <button type="submit">执行Ping测试</button>
                </form>
                <div class="result {result_class}">{result}</div>
                <footer>本工具仅用于网络诊断 | 使用Python构建</footer>
            </div>
        </body>
        </html>
        """
        
        self.wfile.write(result_html.encode('utf-8'))

if __name__ == "__main__":
    with socketserver.TCPServer(("", PORT), PingHandler) as httpd:
        print(f"服务器正在运行,访问地址: http://localhost:{PORT}")
        print("按 Ctrl+C 停止服务器")
        try:
            httpd.serve_forever()
        except KeyboardInterrupt:
            print("\n服务器已停止")
posted @ 2025-07-16 14:33  wanghongwei-dev  阅读(49)  评论(0)    收藏  举报