Python Socket多线程文件传输:终端进度显示适配与多线程并行执行优化问题
Hey there! Let's tackle your two issues one by one, keeping your core functions intact as you requested.
问题1:终端高度不足导致进度显示被覆盖
原因分析
Your current implementation calculates last_used_line_in_terminal to position progress displays, but it doesn't account for the actual terminal window height. When the terminal is too short, new progress lines end up overwriting content at the bottom of the window.
解决方案
We can dynamically adjust the display position by first getting the terminal's actual size, or add a fallback to avoid content overlap. Here's how to modify your code:
- Add a helper function to get terminal height:
def get_terminal_height(): try: return os.get_terminal_size().lines except OSError: # Fallback to a reasonable default if terminal size can't be detected return 24
- Update the
download_filefunction to check terminal height before setting the base line:
def download_file(HOST, PORT, file_name, file_size): global last_used_line_in_terminal os.makedirs(DOWNLOAD_FOLDER, exist_ok=True) threads = [] parts = [] terminal_height = get_terminal_height() # Calculate lines needed for one download: title + 4 progress lines + completion message + separator required_lines = 7 # If terminal is too short, scroll to bottom or reset line counter if last_used_line_in_terminal + required_lines > terminal_height: print("\n" * (terminal_height - last_used_line_in_terminal)) last_used_line_in_terminal = 0 base_line = last_used_line_in_terminal + 1 last_used_line_in_terminal = base_line + 6 print(f"\033[{base_line}HDownloading {file_name}...\n") # Rest of your code remains unchanged...
- Optimize the
display_progress_downloadfunction to avoid cursor out-of-bounds:
def display_progress_download(base_line, part_num, percent): with lock: terminal_height = get_terminal_height() target_line = base_line + part_num # Ensure we don't go beyond the terminal's last line if target_line >= terminal_height: target_line = terminal_height - 1 sys.stdout.write(f'\033[{target_line}H') sys.stdout.write('\033[2K') sys.stdout.write(f"Part {part_num} - Progress: {percent:.2f}% / 100%\r") sys.stdout.flush() time.sleep(0.01)
This will adapt to different terminal sizes and prevent content from being overwritten.
问题2:多线程无法并行传输文件部分
原因分析
Looking at your server code, you're using a global file_lock around file reads:
with file_lock: with open(file_path, 'rb') as f: # File read logic...
This lock forces all threads to access the file sequentially—only one thread can read the file at a time. That's why you see part 1 almost finish before part 2 starts. The good news is reading the same file from multiple threads is completely safe (no data corruption since we're only reading, not writing), so the lock is unnecessary here.
解决方案
Remove the file lock entirely from your server code:
- Delete the global lock declaration:
# Remove this line: file_lock = threading.Lock()
- Remove the lock context manager from the
handle_clientfunction, leaving just the file read:
# Original code: # with file_lock: # with open(file_path, 'rb') as f: # ... # Modified code: with open(file_path, 'rb') as f: f.seek(start) remaining = end - start while remaining > 0: chunk_size = min(BUFFER_SIZE, remaining) chunk = f.read(chunk_size) if not chunk: break server.sendall(chunk) remaining -= len(chunk)
After this change, the server can handle multiple file read requests in parallel, so all four of your client threads will transfer their file parts simultaneously (with minor differences in speed due to network/OS scheduling, which is normal).
备注:内容来源于stack exchange,提问作者hilo

