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"的错误。
修复步骤
- 给监听socket设置超时,打破
accept()阻塞:让accept()定期抛出超时异常,借此检查server_active状态,退出循环。 - 调整关闭顺序:先关闭监听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
相关产品推荐
相关产品推荐

