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

Python C2服务器handleExit()函数异常,无法正常终止程序

C2服务器退出逻辑修复方案

问题描述

本人有C和Java开发背景,正在编写一个C2服务器学习Python网络编程与基础语法,不熟悉Python最佳实践。程序采用递归方式处理命令,需求是捕获CTRL+C或输入exit时终止程序并关闭套接字与线程,但执行后程序仍在后台运行,handleExit()函数无法正常工作。

核心问题点

  • Python线程没有stop()方法,该方法已被废弃,直接调用会报错
  • acceptClients线程是非守护线程,主线程退出后它会继续运行,导致程序残留后台
  • serverSocket.accept()是阻塞调用,未处理时关闭socket无法立刻终止该阻塞
  • handleExit中的资源关闭条件过于严格,需所有资源同时存在才执行关闭,导致部分场景下资源未释放
  • 捕获KeyboardInterrupt时递归调用handleExit,可能引发输入流程异常

修复方案与代码实现

关键修改点

  1. 添加全局退出标志running,用于控制线程循环
  2. 将acceptThread设置为守护线程,主线程退出时自动终止
  3. 替换while True为while running,通过标志位优雅终止线程
  4. 优化handleExit的资源释放逻辑,逐个判断并关闭资源,不再依赖所有资源同时存在
  5. 移除handleExit内的递归调用,改用循环处理用户确认输入
  6. 给serverSocket设置超时,避免accept()永久阻塞

修复后完整代码

import socket
import threading
import sys
from pyfiglet import figlet_format

# 全局变量
serverSocket = None
clientSocket = None
acceptThread = None
clientAddress = None
running = True  # 退出标志位

def handleInput():
    i = input("SmoothArmy => ").lower()
    if i == "exit":
        handleExit()
    elif i == "help":
        helpCommand()
    elif i.startswith("listen"):
        # 处理可选端口参数
        parts = i.split()
        port = int(parts[1]) if len(parts) > 1 else 9001
        startListener(port)
    else:
        print("That command doesn't exist.")
        
def helpCommand():
    print("List of available Commands:\n")
    print("----------------------------------------------------------------------------------------\n")
    print("help                          :    Shows available Commands\n")
    print("exit                          :    To terminate session\n")
    print("listen <port>                 :    Listen on a port\n")
    print("------------------------------------------------------------------------------------------\n")
    
def startListener(port):
    global serverSocket
    global acceptThread
    try:
        if serverSocket is not None:
            print("Listener is already running.")
            return
        
        serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        serverSocket.settimeout(1)  # 设置超时,避免永久阻塞
        serverSocket.bind(("", port))  # 绑定到所有网卡,而非仅本地主机
        serverSocket.listen(10)
        print(f"listening on port {port}...")
        
        # 创建守护线程
        acceptThread = threading.Thread(target=acceptClients, daemon=True)
        acceptThread.start()
    except Exception as e:
        print(f"There was an error starting socket: {e}")
        serverSocket = None
    

def acceptClients():
    global clientSocket
    global clientAddress
    while running:
        try:
            clientSocket, clientAddress = serverSocket.accept()
            print(f"Connection from {clientAddress} has been established!")
        except socket.timeout:
            continue  # 超时后继续循环检查running标志
        except Exception as e:
            if running:
                print(f"Accept error: {e}")
            break
    
def handleExit():
    global running
    while True:
        try:
            i = input("\nDo you want to exit? (y|n) ").lower()
            if i == "y": 
                running = False  # 设置退出标志
                
                # 关闭客户端套接字
                if clientSocket is not None:
                    try:
                        clientSocket.shutdown(socket.SHUT_RDWR)
                        clientSocket.close()
                    except Exception:
                        pass
                    clientSocket = None
                
                # 关闭服务器套接字
                if serverSocket is not None:
                    try:
                        serverSocket.close()
                    except Exception:
                        pass
                    serverSocket = None
                
                # 等待线程结束(可选,因线程是守护线程,主线程退出会自动终止)
                if acceptThread is not None and acceptThread.is_alive():
                    acceptThread.join(timeout=1)
                
                sys.exit(0)
            elif i == "n":
                break
            else:
                print("Please enter y or n.")
        except KeyboardInterrupt:
            print("\nPlease confirm exit with y or n.")

print(figlet_format("SmoothArmy", font="standard"))
print("         __ ")
print(" _(\    |@@|")
print("(__/\__ \--/ ")
print("   \___|----|  |   __")
print("       \ }{ /\ )_ / _\\")
print("       /\__/\ \__O (__")
print("      (--/\--)    \__/")
print("      _)(  )(_")
print("     `---''---`")
print("\n")

while running:
    try: 
        handleInput()
    except KeyboardInterrupt:
        print("\n")
        handleExit()

额外的Python最佳实践建议

  • 尽量减少全局变量的使用,可将相关资源封装到类中(比如C2Server类),更符合面向对象思想,也便于管理状态
  • 异常处理应避免空except,明确捕获具体异常类型,便于排查问题
  • input()在多线程环境下可能出现问题,若后续扩展功能,需考虑线程安全的输入处理

内容的提问来源于stack exchange,提问作者KaoDev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 06:35:20