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

Python SSL TCP Socket持续运行24小时后无响应问题求助

SSL服务器运行24小时后挂起,非SSL线程正常工作

我编写的Python服务器脚本运行约24小时后,SSL TCP服务器会完全停止监听和响应,但进程仍处于运行状态。故障发生前服务器状态一切正常,排查日志未发现任何异常。

  • 非SSL TCP Socket在单独线程运行,从未出现挂起情况,当主线程的SSL TCP Socket挂起时,非SSL线程仍能正常接收并响应客户端请求
  • 两者共用同一个listener()函数,推测问题可能与Python SSL wrapper有关,但无法确定

核心代码如下:

def ssl_server():
    #logger("MAIN", "ssl_server", "SERVER", "")
    #_logger_("MAIN", "ssl_server", "SERVER", "")
    
    global run

    run            = True
    non_ssl_thread = Thread(target = non_ssl_server, args = (server_config["enable-https"]["force-ssl"]["force"],server_config["enable-https"]["force-ssl"]["true-path"]))
    SERVER_HOST    = server_config["server-host"]
    tcp_socket     = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
    
    try:
        if not(server_config["enable-https"]["enabled"]):
            raise Exception("HTTPS Disabled")
        
        SERVER_PORT = 443
        context     = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
        context.load_cert_chain(server_config["cert-location"])
        tcp_socket.bind((SERVER_HOST, SERVER_PORT))
        non_ssl_thread.daemon = True
        non_ssl_thread.start()
        tcp_socket.listen(server_config["queue-limit"])
        ssl_tcp_socket = context.wrap_socket(tcp_socket, server_side=True)
        listener(ssl_tcp_socket)
    except:
        #logger("MAIN", "ssl_server", "SERVER", "EXCEPTION: " + traceback.format_exc().replace('"', '&#34;').replace('<', "&#60;").replace('>', "&#62;").replace('\n', "<br>").replace(' ', "&#160;"))
        
        run = False
        tcp_socket.close()
        non_ssl_server()

    ssl_tcp_socket.close()
    
    try:
        tcp_socket.close()
    except:
        #logger("MAIN", "ssl_server", "SERVER", "EXCEPTION: " + traceback.format_exc().replace('"', '&#34;').replace('<', "&#60;").replace('>', "&#62;").replace('\n', "<br>").replace(' ', "&#160;"))
        pass
#END ssl_server()

def listener(_socket_, redirect_to_ssl = False, true_path = False):
    #logger("MAIN", "listener", "SERVER", "")
    #_logger_("MAIN", "listener", "SERVER", "")
    
    global run
    
    while run:
        try:
            (client_connection, client_address) = _socket_.accept()
            client_request                      = client_connection.recv(server_config["socket-buffer-size"]).decode()
            server_response                     = initialized_handler.respond_to_request(client_request, client_address, redirect_to_ssl, true_path)

            client_connection.sendall(server_response)
            client_connection.close()
        except:
            #logger("MAIN", "listener", "SERVER", "EXCEPTION: " + traceback.format_exc().replace('"', '&#34;').replace('<', "&#60;").replace('>', "&#62;").replace('\n', "<br>").replace(' ', "&#160;"))
            pass
#END listener()

可能的原因分析

  1. SSL握手阶段阻塞:
    SSL连接需要完成握手流程才能进入数据传输,若客户端发起连接后中途断开、恶意挂起或网络异常,可能导致accept()后的SSL握手在底层无限阻塞,直接卡住整个listener循环。而普通TCP连接accept()完成后即可处理数据,没有额外的握手环节,因此不会出现这类阻塞。

  2. 未妥善处理SSL异常与资源泄漏:
    当前代码的except块笼统捕获所有异常但仅注释了日志逻辑,未做任何连接清理或错误恢复。如果SSL相关异常(如握手超时、证书验证失败)导致客户端连接未被正常关闭,会逐渐耗尽Socket资源,最终导致服务器无法接受新连接。

  3. 全局变量run的线程安全问题:
    多线程环境下直接读写全局变量run未加同步锁,可能导致主线程和非SSL线程对run的状态认知不一致,极端情况下可能让SSL的listener循环意外进入假死状态。

修复建议

1. 为SSL客户端连接添加超时限制

在accept()后立即设置客户端连接的超时时间,避免握手或数据接收阶段无限阻塞:

(client_connection, client_address) = _socket_.accept()
# 设置10秒超时,可根据实际场景调整
client_connection.settimeout(10.0)

2. 细化异常处理,确保资源释放

拆分异常类型,针对性处理,同时用finally块确保无论是否出错都关闭客户端连接:

def listener(_socket_, redirect_to_ssl = False, true_path = False):
    global run
    while run:
        client_connection = None
        try:
            (client_connection, client_address) = _socket_.accept()
            client_connection.settimeout(10.0)
            client_request = client_connection.recv(server_config["socket-buffer-size"]).decode()
            server_response = initialized_handler.respond_to_request(client_request, client_address, redirect_to_ssl, true_path)
            client_connection.sendall(server_response)
        except ssl.SSLError as e:
            print(f"SSL错误来自 {client_address}: {str(e)}")
        except socket.timeout:
            print(f"连接超时来自 {client_address}")
        except Exception as e:
            print(f"通用错误: {str(e)}")
        finally:
            # 确保连接被关闭,避免资源泄漏
            if client_connection:
                try:
                    client_connection.close()
                except Exception:
                    pass

3. 保障全局变量run的线程安全

使用threading.Lock同步对run的读写操作:

import threading

# 初始化锁和全局变量
run_lock = threading.Lock()
run = True

# 在修改run时加锁
with run_lock:
    run = False

# 在listener循环中检查run状态时加锁
while True:
    with run_lock:
        if not run:
            break
    # 处理连接逻辑

4. 优化SSLContext配置

确保SSL上下文启用了安全的TLS版本,避免因协议兼容性问题导致握手失败:

context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
# 限制TLS版本范围,提高安全性和兼容性
context.minimum_version = ssl.TLSVersion.TLS_1_2
context.maximum_version = ssl.TLSVersion.TLS_1_3
context.load_cert_chain(server_config["cert-location"])

5. 增加运行时监控

定期用系统工具(如netstat -an)查看服务器的连接状态,排查是否有大量未释放的连接;也可在代码中添加连接计数统计,便于定位资源泄漏问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 13:30:42