直接用socket传文件易卡在半途,因TCP无消息边界,recv()可能收不全或粘包;须自定义协议:先发固定长度文件大小,再发文件体,接收方按长循环recv()累加读满。

为什么直接用 socket 传文件容易卡在半途?
因为 TCP 没有“消息边界”,recv() 可能只收到部分数据、也可能一次收多条,而文件头尾又没固定标记。常见现象是客户端卡在 recv() 阻塞,或接收到错位的二进制内容(比如图片打不开、zip 解压失败)。
必须自己定义协议:先发文件大小(固定长度,如 8 字节),再发文件体。接收方严格按此流程读取:
- 先
recv(8),用int.from_bytes(..., 'big')解出总长度 - 循环调用
recv()累计读满该长度,每次最多读 65536 字节(避免大文件阻塞太久) - 写入文件时用
open(..., 'wb'),别用文本模式
服务端如何避免 bind() 报 [Errno 98] Address already in use?
端口被占用通常不是别的程序占着,而是你上次运行崩溃后 socket 还在 TIME_WAIT 状态。不加处理就直接重启服务会失败。
解决方法是在 socket() 后、bind() 前设置 SO_REUSEADDR:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 关键
s.bind(('0.0.0.0', 8080))
s.listen(1)
注意:SO_REUSEADDR 在 Windows 和 Linux 行为略有差异,但对局域网短连接足够安全;不要用 SO_REUSEPORT(Python 3.8+ 才支持,且非必要)。
客户端怎么知道文件传完了而不提前关闭连接?
很多人用 sendfile() 或简单 send() 发完就 close(),结果服务端 recv() 返回空字节(b''),误判为传输结束——其实只是连接被关了,文件可能只传了一半。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
正确做法是:发送方发完文件后,**单独发一个结束标记**(比如 4 字节的 b'DONE'),接收方读到这个标记才退出循环:
- 服务端发送逻辑:
s.sendall(file_data); s.send(b'DONE') - 客户端接收循环中,每次
recv(4096)后检查末尾是否含b'DONE',如有则截断并退出 - 更健壮的做法是把
DONE放在单独一次send(),接收方先判断len(data) == 4 and data == b'DONE'
别依赖 shutdown(SHUT_WR) —— 它在某些 NAT 或防火墙下不可靠,且和 recv() 的语义不直观匹配。
用 threading 处理多个客户端时,为什么文件总传混?
因为多个线程共用同一个 socket 对象,或者共用同一个文件句柄(open() 返回的对象),导致写入错乱。典型错误是把 f = open(filename, 'wb') 写在主线程里,然后传给子线程。
每个客户端连接必须独占资源:
- 每个线程内重新
open()文件,用唯一文件名(比如加时间戳或客户端地址) - 不要在线程间共享
socket实例;accept()返回的新conn就是线程私有的 - 如果要用线程池,确保
submit()传的是原始字节数据 + 目标路径,而不是文件对象
更简单的替代方案是用 asyncio + asyncio.start_server(),天然避免共享状态问题,但调试难度略高。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










