Administrator
发布于 2025-03-04 / 17 阅读 / 0 评论 / 0 点赞

Windows上安装Socat的方法

import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox, simpledialog
import socket
import threading
import select
import sys
import queue
import time
import datetime
import json
import os


class RedirectText:
    def __init__(self, text_widget):
        self.text_widget = text_widget
        self.queue = queue.Queue()
        self.updating = True
        threading.Thread(target=self.update_widget_loop, daemon=True).start()

    def write(self, string):
        self.queue.put(string)

    def flush(self):
        pass

    def update_widget_loop(self):
        while self.updating:
            try:
                while True:
                    text = self.queue.get_nowait()
                    timestamp = datetime.datetime.now().strftime("[%H:%M:%S] ")
                    self.text_widget.configure(state="normal")
                    self.text_widget.insert(tk.END, timestamp + text)
                    self.text_widget.see(tk.END)
                    self.text_widget.configure(state="disabled")
                    self.queue.task_done()
            except queue.Empty:
                time.sleep(0.1)

    def stop(self):
        self.updating = False


class Connection:
    def __init__(self, source, destination, id_str):
        self.source = source
        self.destination = destination
        self.id = id_str
        self.active = True
        self.bytes_transferred = 0

    def handle(self):
        try:
            while self.active:
                try:
                    readable, _, _ = select.select([self.source], [], [], 0.5)
                    if self.source in readable:
                        data = self.source.recv(4096)
                        if not data:
                            break
                        self.destination.sendall(data)
                        self.bytes_transferred += len(data)
                except Exception as e:
                    print(f"传输错误 {self.id}: {e}")
                    break
        except:
            pass
        finally:
            self.close()

    def close(self):
        self.active = False
        try:
            self.source.close()
        except:
            pass
        try:
            self.destination.close()
        except:
            pass
        print(f"连接 {self.id} 已关闭。传输字节: {self.bytes_transferred}")


class ConfigManager:
    def __init__(self, config_file="socat_configs.json"):
        self.config_file = config_file
        self.configs = self.load_configs()

    def load_configs(self):
        if os.path.exists(self.config_file):
            try:
                with open(self.config_file, "r", encoding="utf-8") as f:
                    return json.load(f)
            except:
                return {}
        return {}

    def save_configs(self):
        with open(self.config_file, "w", encoding="utf-8") as f:
            json.dump(self.configs, f, indent=4, ensure_ascii=False)

    def add_config(self, name, config_data):
        self.configs[name] = config_data
        self.save_configs()

    def remove_config(self, name):
        if name in self.configs:
            del self.configs[name]
            self.save_configs()

    def get_config(self, name):
        return self.configs.get(name)

    def get_config_names(self):
        return list(self.configs.keys())

    def get_all_configs(self):
        return self.configs


class SocatApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Python Socat GUI - 多配置表格版")
        self.root.geometry("1200x700")
        self.root.resizable(True, True)

        # 设置主题
        style = ttk.Style()
        if 'clam' in style.theme_names():
            style.theme_use('clam')

        # 创建样式
        style.configure('TFrame', background='#f0f0f0')
        style.configure('TLabel', background='#f0f0f0', font=('Arial', 10))
        style.configure('TButton', font=('Arial', 10))
        style.configure('Header.TLabel', font=('Arial', 12, 'bold'))

        # 表格样式
        style.configure("Treeview", font=('Arial', 10))
        style.configure("Treeview.Heading", font=('Arial', 10, 'bold'))

        # 编辑窗口变量
        self.name_var = tk.StringVar()
        self.mode_var = tk.StringVar(value="TCP")
        self.listen_protocol_var = tk.StringVar(value="IPv4-UDP")
        self.listen_port_var = tk.StringVar(value="8000")
        self.target_protocol_var = tk.StringVar(value="IPv4-UDP")
        self.target_addr_var = tk.StringVar(value="127.0.0.1")
        self.target_port_var = tk.StringVar(value="9000")
        self.firewall_enabled_var = tk.BooleanVar(value=True)

        # 状态变量
        self.status_var = tk.StringVar(value="就绪")

        # 配置管理器
        self.config_manager = ConfigManager()

        # 服务状态
        self.active_configs = []  # 存储当前活动的配置

        # 创建UI
        self.create_widgets()
        self.update_config_table()

    def create_widgets(self):
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 上部 - 表格和控制按钮
        top_frame = ttk.Frame(main_frame)
        top_frame.pack(fill=tk.BOTH, expand=True, pady=5)

        # 创建表格视图
        table_frame = ttk.Frame(top_frame)
        table_frame.pack(fill=tk.BOTH, expand=True)

        # 创建表格和滚动条
        self.tree_scroll = ttk.Scrollbar(table_frame)
        self.tree_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        self.config_tree = ttk.Treeview(table_frame, columns=(
            "status", "name", "listen_protocol", "listen_port",
            "target_protocol", "target_addr", "target_port", "firewall"
        ), show="headings", selectmode="browse")

        self.config_tree.column("status", width=60, anchor="center")
        self.config_tree.column("name", width=120, anchor="w")
        self.config_tree.column("listen_protocol", width=120, anchor="center")
        self.config_tree.column("listen_port", width=100, anchor="center")
        self.config_tree.column("target_protocol", width=120, anchor="center")
        self.config_tree.column("target_addr", width=150, anchor="center")
        self.config_tree.column("target_port", width=100, anchor="center")
        self.config_tree.column("firewall", width=150, anchor="center")

        self.config_tree.heading("status", text="状态")
        self.config_tree.heading("name", text="备注")
        self.config_tree.heading("listen_protocol", text="监听协议")
        self.config_tree.heading("listen_port", text="监听端口")
        self.config_tree.heading("target_protocol", text="目标协议")
        self.config_tree.heading("target_addr", text="目标地址")
        self.config_tree.heading("target_port", text="目标端口")
        self.config_tree.heading("firewall", text="打开防火墙端口")

        self.config_tree.pack(fill=tk.BOTH, expand=True)
        self.tree_scroll.config(command=self.config_tree.yview)
        self.config_tree.configure(yscrollcommand=self.tree_scroll.set)

        # 表格操作按钮框架
        button_frame = ttk.Frame(top_frame)
        button_frame.pack(fill=tk.X, pady=10)

        ttk.Button(button_frame, text="添加", command=self.show_add_dialog, width=10).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="编辑", command=self.show_edit_dialog, width=10).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="删除", command=self.delete_config, width=10).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="启动", command=self.start_config, width=10).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="停止", command=self.stop_config, width=10).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="清除日志", command=self.clear_log, width=10).pack(side=tk.RIGHT, padx=5)

        # 下部 - 日志区域
        log_frame = ttk.LabelFrame(main_frame, text="日志")
        log_frame.pack(fill=tk.BOTH, expand=True, pady=5)

        self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, state=tk.DISABLED, height=10)
        self.log_text.pack(fill=tk.BOTH, expand=True)

        # 状态栏
        status_frame = ttk.Frame(main_frame)
        status_frame.pack(fill=tk.X, side=tk.BOTTOM, pady=5)

        ttk.Label(status_frame, text="状态:").pack(side=tk.LEFT)
        ttk.Label(status_frame, textvariable=self.status_var).pack(side=tk.LEFT, padx=5)

        # 重定向标准输出
        self.redirect = RedirectText(self.log_text)
        sys.stdout = self.redirect

        # 退出处理
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)

    def show_add_dialog(self):
        # 重置变量
        self.name_var.set("")
        self.mode_var.set("TCP")
        self.listen_protocol_var.set("IPv4-UDP")
        self.listen_port_var.set("8000")
        self.target_protocol_var.set("IPv4-UDP")
        self.target_addr_var.set("127.0.0.1")
        self.target_port_var.set("9000")
        self.firewall_enabled_var.set(True)

        # 创建对话框
        self.create_edit_dialog("添加配置")

    def show_edit_dialog(self):
        selected = self.config_tree.selection()
        if not selected:
            messagebox.showinfo("提示", "请先选择一个配置")
            return

        # 获取选中项的配置名称
        item_values = self.config_tree.item(selected[0], "values")
        config_name = item_values[1]  # 名称在第2列

        # 获取配置详情
        config = self.config_manager.get_config(config_name)
        if not config:
            messagebox.showerror("错误", f"找不到配置: {config_name}")
            return

        # 设置变量
        self.name_var.set(config.get("name", ""))
        self.mode_var.set(config.get("mode", "TCP"))
        self.listen_protocol_var.set(config.get("listen_protocol", "IPv4-UDP"))
        self.listen_port_var.set(config.get("listen_port", ""))
        self.target_protocol_var.set(config.get("target_protocol", "IPv4-UDP"))
        self.target_addr_var.set(config.get("target_addr", ""))
        self.target_port_var.set(config.get("target_port", ""))
        self.firewall_enabled_var.set(config.get("firewall_enabled", True))

        # 创建对话框
        self.create_edit_dialog("编辑配置", config_name)

    def create_edit_dialog(self, title, old_name=None):
        dialog = tk.Toplevel(self.root)
        dialog.title(title)
        dialog.geometry("500x350")
        dialog.transient(self.root)
        dialog.grab_set()

        # 主框架
        frame = ttk.Frame(dialog, padding="10")
        frame.pack(fill=tk.BOTH, expand=True)

        # 表单元素
        ttk.Label(frame, text="备注:").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5)
        ttk.Entry(frame, textvariable=self.name_var, width=30).grid(row=0, column=1, columnspan=3, sticky=tk.W, padx=5,
                                                                    pady=5)

        ttk.Label(frame, text="模式:").grid(row=1, column=0, sticky=tk.W, padx=5, pady=5)
        ttk.Radiobutton(frame, text="TCP", variable=self.mode_var, value="TCP").grid(row=1, column=1, sticky=tk.W)
        ttk.Radiobutton(frame, text="UDP", variable=self.mode_var, value="UDP").grid(row=1, column=2, sticky=tk.W)

        ttk.Label(frame, text="监听协议:").grid(row=2, column=0, sticky=tk.W, padx=5, pady=5)
        protocol_combo1 = ttk.Combobox(frame, textvariable=self.listen_protocol_var,
                                       values=["IPv4-TCP", "IPv4-UDP", "IPv6-TCP", "IPv6-UDP"], state="readonly")
        protocol_combo1.grid(row=2, column=1, columnspan=3, sticky=tk.W, padx=5, pady=5)

        ttk.Label(frame, text="监听端口:").grid(row=3, column=0, sticky=tk.W, padx=5, pady=5)
        ttk.Entry(frame, textvariable=self.listen_port_var, width=15).grid(row=3, column=1, columnspan=3, sticky=tk.W,
                                                                           padx=5, pady=5)

        ttk.Label(frame, text="目标协议:").grid(row=4, column=0, sticky=tk.W, padx=5, pady=5)
        protocol_combo2 = ttk.Combobox(frame, textvariable=self.target_protocol_var,
                                       values=["IPv4-TCP", "IPv4-UDP", "IPv6-TCP", "IPv6-UDP"], state="readonly")
        protocol_combo2.grid(row=4, column=1, columnspan=3, sticky=tk.W, padx=5, pady=5)

        ttk.Label(frame, text="目标地址:").grid(row=5, column=0, sticky=tk.W, padx=5, pady=5)
        ttk.Entry(frame, textvariable=self.target_addr_var, width=30).grid(row=5, column=1, columnspan=3, sticky=tk.W,
                                                                           padx=5, pady=5)

        ttk.Label(frame, text="目标端口:").grid(row=6, column=0, sticky=tk.W, padx=5, pady=5)
        ttk.Entry(frame, textvariable=self.target_port_var, width=15).grid(row=6, column=1, columnspan=3, sticky=tk.W,
                                                                           padx=5, pady=5)

        ttk.Checkbutton(frame, text="开启防火墙端口", variable=self.firewall_enabled_var).grid(row=7, column=0,
                                                                                               columnspan=4,
                                                                                               sticky=tk.W, padx=5,
                                                                                               pady=5)

        # 按钮
        button_frame = ttk.Frame(frame)
        button_frame.grid(row=8, column=0, columnspan=4, pady=10)

        ttk.Button(button_frame, text="保存", command=lambda: self.save_config_dialog(dialog, old_name), width=10).pack(
            side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="取消", command=dialog.destroy, width=10).pack(side=tk.LEFT, padx=5)

    def save_config_dialog(self, dialog, old_name=None):
        # 验证输入
        name = self.name_var.get().strip()
        listen_port = self.listen_port_var.get().strip()
        target_addr = self.target_addr_var.get().strip()
        target_port = self.target_port_var.get().strip()

        if not name:
            messagebox.showerror("错误", "请输入备注名称", parent=dialog)
            return

        try:
            listen_port_int = int(listen_port)
            if not (1 <= listen_port_int <= 65535):
                raise ValueError("Port out of range")
        except:
            messagebox.showerror("错误", "监听端口必须是1-65535之间的数字", parent=dialog)
            return

        if not target_addr:
            messagebox.showerror("错误", "请输入目标地址", parent=dialog)
            return

        try:
            target_port_int = int(target_port)
            if not (1 <= target_port_int <= 65535):
                raise ValueError("Port out of range")
        except:
            messagebox.showerror("错误", "目标端口必须是1-65535之间的数字", parent=dialog)
            return

        # 检查是否需要改名
        if old_name and name != old_name:
            # 如果旧名称正在运行,则不允许更改
            for config in self.active_configs:
                if config.get("name") == old_name:
                    messagebox.showerror("错误", f"配置 '{old_name}' 正在运行中,无法更改名称", parent=dialog)
                    return

            # 删除旧配置
            self.config_manager.remove_config(old_name)

        # 检查名称是否已存在(仅在添加或更改名称时检查)
        if (not old_name or name != old_name) and name in self.config_manager.get_config_names():
            messagebox.showerror("错误", f"配置名称 '{name}' 已存在", parent=dialog)
            return

        # 创建配置数据
        config = {
            "name": name,
            "mode": self.mode_var.get(),
            "listen_protocol": self.listen_protocol_var.get(),
            "listen_port": listen_port,
            "target_protocol": self.target_protocol_var.get(),
            "target_addr": target_addr,
            "target_port": target_port,
            "firewall_enabled": self.firewall_enabled_var.get()
        }

        # 保存配置
        self.config_manager.add_config(name, config)

        # 更新表格
        self.update_config_table()

        # 关闭对话框
        dialog.destroy()

    def delete_config(self):
        selected = self.config_tree.selection()
        if not selected:
            messagebox.showinfo("提示", "请先选择一个配置")
            return

        # 获取选中项的配置名称
        item_values = self.config_tree.item(selected[0], "values")
        config_name = item_values[1]  # 名称在第2列

        # 检查配置是否正在运行
        for config in self.active_configs:
            if config.get("name") == config_name:
                messagebox.showerror("错误", f"配置 '{config_name}' 正在运行,无法删除")
                return

        if messagebox.askyesno("确认删除", f"确定要删除配置 '{config_name}' 吗?"):
            self.config_manager.remove_config(config_name)
            self.update_config_table()

    def update_config_table(self):
        # 清空表格
        for item in self.config_tree.get_children():
            self.config_tree.delete(item)

        # 添加配置到表格
        for name, config in self.config_manager.get_all_configs().items():
            # 检查配置是否在运行
            status = "✓" if any(ac.get("name") == name for ac in self.active_configs) else ""

            self.config_tree.insert("", "end", values=(
                status,
                config.get("name", ""),
                config.get("listen_protocol", ""),
                config.get("listen_port", ""),
                config.get("target_protocol", ""),
                config.get("target_addr", ""),
                config.get("target_port", ""),
                "✓" if config.get("firewall_enabled", True) else ""
            ))

    def create_tcp_listener(self, address, port):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind((address, port))
        sock.listen(5)
        return sock

    def create_udp_socket(self, address, port):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind((address, port))
        return sock

    def connect_to_tcp(self, address, port):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((address, port))
        return sock

    def run_tcp_server(self, config):
        is_ipv6 = "IPv6" in config.get("listen_protocol", "")
        listen_addr = "::" if is_ipv6 else "0.0.0.0"
        listen_port = int(config.get("listen_port", 8000))

        target_is_ipv6 = "IPv6" in config.get("target_protocol", "")
        target_addr = config.get("target_addr", "127.0.0.1")
        target_port = int(config.get("target_port", 9000))

        socket_family = socket.AF_INET6 if is_ipv6 else socket.AF_INET

        print(f"[{config['name']}] TCP监听模式: 监听 {listen_addr}:{listen_port}, 转发到 {target_addr}:{target_port}")

        try:
            # 创建服务器套接字
            server_socket = socket.socket(socket_family, socket.SOCK_STREAM)
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            if is_ipv6:
                server_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
            server_socket.bind((listen_addr, listen_port))
            server_socket.listen(5)

            config["server_socket"] = server_socket
            conn_count = 0

            while config.get("running", False):
                try:
                    server_socket.settimeout(1.0)  # 设置超时,以便能够检测停止标志
                    client_sock, client_addr = server_socket.accept()
                    conn_count += 1
                    conn_id = f"{config['name']}-{client_addr[0]}:{client_addr[1]}-{conn_count}"

                    print(f"[{config['name']}] 新连接来自 {client_addr[0]}:{client_addr[1]}")

                    try:
                        # 连接到目标
                        target_family = socket.AF_INET6 if target_is_ipv6 else socket.AF_INET
                        target_sock = socket.socket(target_family, socket.SOCK_STREAM)
                        target_sock.connect((target_addr, target_port))

                        # 创建两个方向的连接
                        conn1 = Connection(client_sock, target_sock, f"{conn_id}-forward")
                        conn2 = Connection(target_sock, client_sock, f"{conn_id}-backward")

                        if "connections" not in config:
                            config["connections"] = []

                        config["connections"].append(conn1)
                        config["connections"].append(conn2)

                        threading.Thread(target=conn1.handle, daemon=True).start()
                        threading.Thread(target=conn2.handle, daemon=True).start()
                    except Exception as e:
                        print(f"[{config['name']}] 连接到目标失败: {e}")
                        client_sock.close()
                except socket.timeout:
                    continue
                except Exception as e:
                    if config.get("running", False):
                        print(f"[{config['name']}] 接受连接时出错: {e}")
        except Exception as e:
            print(f"[{config['name']}] 服务器错误: {e}")
        finally:
            self.cleanup_config(config)
            self.root.after(0, self.update_config_table)
            print(f"[{config['name']}] 服务已停止")

    def run_udp_server(self, config):
        is_ipv6 = "IPv6" in config.get("listen_protocol", "")
        listen_addr = "::" if is_ipv6 else "0.0.0.0"
        listen_port = int(config.get("listen_port", 8000))

        target_is_ipv6 = "IPv6" in config.get("target_protocol", "")
        target_addr = config.get("target_addr", "127.0.0.1")
        target_port = int(config.get("target_port", 9000))

        socket_family = socket.AF_INET6 if is_ipv6 else socket.AF_INET
        target_family = socket.AF_INET6 if target_is_ipv6 else socket.AF_INET

        print(f"[{config['name']}] UDP转发模式: 监听 {listen_addr}:{listen_port}, 转发到 {target_addr}:{target_port}")

        try:
            # 创建UDP套接字
            server_socket = socket.socket(socket_family, socket.SOCK_DGRAM)
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            if is_ipv6:
                server_socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
            server_socket.bind((listen_addr, listen_port))

            # 创建目标套接字
            target_socket = socket.socket(target_family, socket.SOCK_DGRAM)

            config["server_socket"] = server_socket
            config["target_socket"] = target_socket

            # 客户端地址映射
            client_map = {}

            # 设置非阻塞模式
            server_socket.setblocking(False)

            while config.get("running", False):
                try:
                    # 使用select监控套接字
                    readable, _, _ = select.select([server_socket], [], [], 1.0)

                    if server_socket in readable:
                        # 接收数据
                        data, client_addr = server_socket.recvfrom(4096)

                        # 记录客户端地址
                        client_addr_str = f"{client_addr[0]}:{client_addr[1]}"
                        if client_addr_str not in client_map:
                            print(f"[{config['name']}] 新UDP客户端: {client_addr_str}")
                            client_map[client_addr_str] = datetime.datetime.now()

                        # 转发到目标
                        target_socket.sendto(data, (target_addr, target_port))

                    # 检查目标返回的数据
                    try:
                        target_socket.setblocking(False)
                        data, _ = target_socket.recvfrom(4096)

                        # 发送给所有客户端
                        for client_str, last_time in list(client_map.items()):
                            # 移除超过60秒未活动的客户端
                            if (datetime.datetime.now() - last_time).total_seconds() > 60:
                                del client_map[client_str]
                                continue

                            # 解析客户端地址
                            host, port = client_str.rsplit(':', 1)
                            server_socket.sendto(data, (host, int(port)))
                    except:
                        pass
                except Exception as e:
                    if config.get("running", False):
                        print(f"[{config['name']}] UDP处理错误: {e}")
        except Exception as e:
            print(f"[{config['name']}] UDP服务器错误: {e}")
        finally:
            self.cleanup_config(config)
            self.root.after(0, self.update_config_table)
            print(f"[{config['name']}] UDP服务已停止")

    def start_config(self):
        selected = self.config_tree.selection()
        if not selected:
            messagebox.showinfo("提示", "请先选择一个配置")
            return

        # 获取选中项的配置名称
        item_values = self.config_tree.item(selected[0], "values")
        config_name = item_values[1]  # 名称在第2列

        # 检查配置是否已在运行
        for config in self.active_configs:
            if config.get("name") == config_name:
                messagebox.showinfo("提示", f"配置 '{config_name}' 已经在运行")
                return

        # 获取配置
        config = self.config_manager.get_config(config_name)
        if not config:
            messagebox.showerror("错误", f"找不到配置: {config_name}")
            return

        # 创建运行时配置
        runtime_config = config.copy()
        runtime_config["running"] = True
        runtime_config["connections"] = []

        # 确定运行模式
        is_tcp = "TCP" in config.get("mode", "TCP")

        # 启动服务器线程
        if is_tcp:
            server_thread = threading.Thread(
                target=self.run_tcp_server,
                args=(runtime_config,),
                daemon=True
            )
        else:
            server_thread = threading.Thread(
                target=self.run_udp_server,
                args=(runtime_config,),
                daemon=True
            )

        runtime_config["thread"] = server_thread

        # 添加到活动配置列表
        self.active_configs.append(runtime_config)

        # 启动线程
        server_thread.start()

        # 更新UI
        self.update_config_table()
        self.status_var.set(f"运行中 - {config_name}")

        print(f"配置 '{config_name}' 已启动")

    def stop_config(self):
        selected = self.config_tree.selection()
        if not selected:
            messagebox.showinfo("提示", "请先选择一个配置")
            return

        # 获取选中项的配置名称
        item_values = self.config_tree.item(selected[0], "values")
        config_name = item_values[1]  # 名称在第2列

        # 查找活动配置
        for config in self.active_configs:
            if config.get("name") == config_name:
                config["running"] = False
                print(f"正在停止配置 '{config_name}'...")
                return

        messagebox.showinfo("提示", f"配置 '{config_name}' 未在运行")

    def cleanup_config(self, config):
        # 关闭所有连接
        if "connections" in config:
            for conn in config["connections"]:
                conn.close()
            config["connections"] = []

        # 关闭服务器套接字
        if "server_socket" in config:
            try:
                config["server_socket"].close()
            except:
                pass
            config["server_socket"] = None

        # 关闭目标套接字 (UDP模式)
        if "target_socket" in config:
            try:
                config["target_socket"].close()
            except:
                pass
            config["target_socket"] = None

        # 从活动列表中移除
        config["running"] = False
        self.active_configs = [c for c in self.active_configs if c.get("name") != config.get("name")]

    def clear_log(self):
        self.log_text.configure(state="normal")
        self.log_text.delete(1.0, tk.END)
        self.log_text.configure(state="disabled")

    def on_closing(self):
        # 停止所有活动的配置
        for config in list(self.active_configs):
            config["running"] = False

        # 等待一小段时间让服务器线程结束
        time.sleep(0.5)

        # 恢复标准输出
        sys.stdout = sys.__stdout__
        if hasattr(self, 'redirect'):
            self.redirect.stop()

        self.root.destroy()


if __name__ == "__main__":
    root = tk.Tk()
    app = SocatApp(root)
    root.mainloop()

评论