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

Tkinter+Socket屏幕共享客户端仅显示首帧截图且崩溃的修复方案

修复屏幕共享客户端的首帧停滞与白屏崩溃问题

核心问题分析

  • 线程创建错误:客户端启动线程时直接执行了sharescreen(client_socket),而非传递函数引用,导致主线程被阻塞。
  • 图片接收逻辑缺陷:客户端receiving_image死循环且无接收终止判断,Server发送完图片后客户端会一直卡在recv,导致程序假死。
  • UI更新未循环触发:change_background仅执行一次,没有通过Tkinter的after机制循环调用,无法更新后续帧。
  • Label未初始化:全局变量label未创建就调用config,引发隐性错误导致白屏崩溃。
  • Server无数据边界标记:Server直接发送图片字节,Client无法判断单张图片的结束位置,导致接收混乱。

修复方案(优先客户端调整,辅以Server端适配)

客户端代码修改

import socket
import tkinter as tk
from PIL import ImageTk, Image
import pyautogui
from threading import Thread
import struct  # 用于处理数据长度打包

label = None
root = None  # 全局保存Tk根窗口引用

def client_program():
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.connect(("127.0.0.1", 2000))
    # 修复:线程创建时传递函数引用和参数,而非直接执行函数
    thread = Thread(target=sharescreen, args=(client_socket,))
    thread.daemon = True  # 设置守护线程,随主线程退出
    thread.start()
    thread.join()

def receiving_image(client_socket):
    # 修复:先接收图片长度,再接收对应字节数的数据,避免无限阻塞
    try:
        # 接收4字节的长度数据(对应struct.pack('!I', size))
        size_data = client_socket.recv(4)
        if not size_data:
            return False
        image_size = struct.unpack('!I', size_data)[0]
        
        file = open("screenshot.png", "wb")
        received_bytes = 0
        while received_bytes < image_size:
            chunk = client_socket.recv(min(1024, image_size - received_bytes))
            if not chunk:
                break
            file.write(chunk)
            received_bytes += len(chunk)
        file.close()
        return received_bytes == image_size
    except Exception as e:
        print(f"接收图片出错: {e}")
        return False

def resize_image(window_width, window_height):
    try:
        image = Image.open("screenshot.png")   
        resized_image = image.resize((window_width, window_height), Image.Resampling.LANCZOS)  # 优化缩放清晰度
        photo_image = ImageTk.PhotoImage(resized_image)
        return photo_image
    except Exception as e:
        print(f"图片处理出错: {e}")
        return None

def sharescreen(client_socket):
    global label, root
    root = tk.Tk()
    root.attributes('-fullscreen', True)
    # 初始化Label用于显示截图
    label = tk.Label(root)
    label.pack(fill=tk.BOTH, expand=True)
    # 立即触发第一次更新,之后循环调用
    change_background(client_socket)
    root.mainloop()

def change_background(client_socket):
    global label, root
    # 接收成功才更新UI
    if receiving_image(client_socket):
        screen_width, screen_height = pyautogui.size()
        photo_image = resize_image(screen_width, screen_height)
        if photo_image:
            label.config(image=photo_image)
            label.image = photo_image  # 保留引用避免被GC回收
    # 修复:每隔1秒循环调用自身,实现持续更新
    root.after(1000, change_background, client_socket)

if __name__ == "__main__":
    client_program()

服务端代码修改(适配客户端的长度校验逻辑)

import socket
import pyautogui
import time
import struct  # 用于打包图片长度

def victim_program():
    victim_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    victim_socket.bind(("127.0.0.1", 2000))
    print("Listening")
    victim_socket.listen(1)
    client, _ = victim_socket.accept()
    print("Connected")
    try:
        while True:
            share_screen_victim(client)
            time.sleep(1)
    except Exception as e:
        print(f"连接断开: {e}")
        client.close()

def share_screen_victim(client):
    screenshot = pyautogui.screenshot()
    screenshot_path = "screenshot.png"
    screenshot.save(screenshot_path)    
    send_image(client, screenshot_path)

def send_image(client, file_path):
    try:
        with open(file_path, "rb") as file:
            data = file.read()
            # 先发送图片长度(4字节无符号整数,网络字节序)
            client.sendall(struct.pack('!I', len(data)))
            # 再发送图片数据
            client.sendall(data)
    except Exception as e:
        print(f"发送图片出错: {e}")

if __name__ == "__main__":
    victim_program()

关键修复点说明

  1. 线程启动方式修正:使用Thread(target=sharescreen, args=(client_socket,))替代直接执行函数,确保UI线程在独立线程中运行。
  2. 数据边界控制:通过struct打包/解包图片长度,让客户端明确知道每张图片的接收字节数,避免无限阻塞。
  3. UI循环更新:在change_background末尾调用root.after(1000, change_background, client_socket),实现每秒一次的截图更新。
  4. Label初始化:在sharescreen中创建并显示Label,避免未定义引用错误。
  5. 异常捕获:添加基础异常处理,方便排查运行时错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 21:33:22