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

Python3中Socket无法关闭/终止的问题求助

服务器Socket关闭错误排查

问题描述

我有一个名为server的socket对象,调用server.shutdown(socket.SHUT_RDWR)时出现错误:

OSError: [WinError 10057] A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied

尝试调用.close()时,出现以下错误:

OSError: [WinError 10038] an operation was attempted on something that is not a socket

是不是我关闭服务器的方式不正确?


服务端代码

import sys
import time
import socket
import threading
import tkinter as tk
from tkinter import messagebox as msgbox

HEADER = 64
PORT = 5050
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "%client_disconnect"

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)
server_active = True

messages = []

def add_message(message):
    local_t = time.localtime()
    sent_at = time.strftime("%Y.%m.%d %H:%M:%S", local_t)
    messages.append(f"{sent_at} | {str(message)}")

def handle_client(conn, addr):
    print(f"\n===[DEBUG (SERVER)]\tHandling connection: {addr}")

    connected = True
    while connected:
        msg_len = conn.recv(HEADER).decode(FORMAT)
        if msg_len:
            msg_len = int(msg_len)
            msg = conn.recv(msg_len).decode(FORMAT)
            print(f"\n===[DEBUG ({addr})]\tMessage length: {msg_len}; Message: {msg}")

            if msg == DISCONNECT_MESSAGE:
                connected = False

        if not server_active:
            connected = False

    conn.close()

def start():
    server.listen()
    print(f"\n===[DEBUG (SERVER)]\tListening on {SERVER}")
    while True:
        print("\n===[DEBUG (SERVER)]\tWaiting for client connection")
        conn, addr = server.accept()
        thread = threading.Thread(target=handle_client, args=(conn, addr))
        thread.start()
        print(f"\n===[DEBUG (SERVER)]\tNew connection: ({addr}); "
              f"Current active connections: {threading.active_count() - 1}")

def on_closing():
    global server_active
    if msgbox.askokcancel("Serverside", "Are you sure you want to quit? This will fully shut down the server"
                                        "and disconnect all clients."):
        server_active = False
        window.destroy()
        server.close()

# window is empty
window = tk.Tk()
window.wm_geometry("700x500")
window.title("Serverside")
window.protocol("WM_DELETE_WINDOW", on_closing)

t = time.localtime()
start_time = time.strftime("%Y.%m.%d %H:%M:%S", t)

print("===[DEBUG (SERVER)]\tStarting Server")
server_thread = threading.Thread(target=start)
server_thread.start()
add_message(f"Server started at: {start_time}")

tk.mainloop()

客户端代码

import socket
import threading
import tkinter as tk
from tkinter import messagebox as msgbox

HEADER = 64
PORT = 5050
FORMAT = 'utf-8'
DISCONNECT_MESSAGE = "%client_disconnect"
SERVER = None
ADDR = None

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

def send(msg):
    message = msg.encode(FORMAT)
    msg_len = len(message)
    send_len = str(msg_len).encode(FORMAT)
    send_len += b' ' * (HEADER - len(send_len))
    client.send(send_len)
    client.send(message)

def connect_to(server):
    global SERVER, ADDR
    SERVER = server
    ADDR = (SERVER, PORT)
    client.connect(ADDR)

def on_closing():
    if msgbox.askokcancel("Client", "Are you sure you want to quit? You will be disconnected from the server."):
        send(DISCONNECT_MESSAGE)
        window.destroy()

def handle_messages():
    msg = str(entry.get())
    send(msg)

    if msg == DISCONNECT_MESSAGE:
        window.destroy()

connect_to(socket.gethostbyname(socket.gethostname()))

window = tk.Tk()
window.geometry("600x450")
window.title("Client")
window.protocol("WM_DELETE_WINDOW", on_closing)

entry = tk.Entry()
entry.place(x=0, y=0, width=100, height=30)

button = tk.Button(command=handle_messages)
button.place(x=0, y=50, width=50, height=20)

tk.mainloop()

问题原因及解决方法

核心问题

你的关闭逻辑存在两个关键问题:

  • server.accept()阻塞导致无法响应关闭信号:start()函数里的while True循环一直卡在server.accept()(阻塞方法),设置server_active = False后,线程无法跳出循环,主线程直接调用server.close()会触发错误。
  • 关闭顺序混乱:先销毁窗口再关闭socket,此时线程状态已混乱,可能导致socket对象失效,触发"不是socket"的错误。

修复步骤

  1. 给监听socket设置超时,打破accept()阻塞:让accept()定期抛出超时异常,借此检查server_active状态,退出循环。
  2. 调整关闭顺序:先关闭监听socket,再等待客户端线程结束,最后销毁窗口。

修改后的关键代码

调整start()函数

def start():
    server.listen()
    server.settimeout(1)  # 设置1秒超时,定期检查状态
    print(f"\n===[DEBUG (SERVER)]\tListening on {SERVER}")
    while server_active:  # 用server_active控制循环
        try:
            print("\n===[DEBUG (SERVER)]\tWaiting for client connection")
            conn, addr = server.accept()
            thread = threading.Thread(target=handle_client, args=(conn, addr))
            thread.start()
            print(f"\n===[DEBUG (SERVER)]\tNew connection: ({addr}); "
                  f"Current active connections: {threading.active_count() - 1}")
        except socket.timeout:
            # 超时后继续循环,检查server_active
            continue
    print("\n===[DEBUG (SERVER)]\tStopping listener...")

调整on_closing()函数

def on_closing():
    global server_active
    if msgbox.askokcancel("Serverside", "Are you sure you want to quit? This will fully shut down the server"
                                        "and disconnect all clients."):
        server_active = False
        # 先关闭监听socket,打破accept阻塞
        server.close()
        # 等待所有客户端线程结束(排除主线程和server_thread)
        while threading.active_count() > 2:
            time.sleep(0.1)
        # 最后销毁窗口
        window.destroy()

额外优化

在handle_client()中,服务器关闭时主动给客户端发送断开信号,确保客户端正常退出:

def handle_client(conn, addr):
    print(f"\n===[DEBUG (SERVER)]\tHandling connection: {addr}")

    connected = True
    while connected:
        try:
            msg_len = conn.recv(HEADER).decode(FORMAT)
            if msg_len:
                msg_len = int(msg_len)
                msg = conn.recv(msg_len).decode(FORMAT)
                print(f"\n===[DEBUG ({addr})]\tMessage length: {msg_len}; Message: {msg}")

                if msg == DISCONNECT_MESSAGE:
                    connected = False

            if not server_active:
                # 主动告知客户端服务器关闭
                send_len = str(len(DISCONNECT_MESSAGE)).encode(FORMAT)
                send_len += b' ' * (HEADER - len(send_len))
                conn.send(send_len)
                conn.send(DISCONNECT_MESSAGE.encode(FORMAT))
                connected = False
        except Exception as e:
            print(f"\n===[DEBUG ({addr})]\tConnection error: {e}")
            connected = False

    conn.close()
    print(f"\n===[DEBUG (SERVER)]\tDisconnected from {addr}")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 10:35:47