如何在Python中通过Socket发送图片、音视频等非基础数据类型
用Python Socket发送图片、音视频等二进制数据的实现方案
嘿,这个问题我之前做项目的时候刚好碰到过!其实socket本身就是基于字节流传输的,不管是字符串还是图片、视频这类可视化数据,本质上都要转成bytes类型来发送——和你想的沿用字符串发送逻辑的思路完全一致,只是处理二进制文件的方式略有不同,不用复杂的类型转换函数,直接操作字节流就好。
核心思路
所有非文本类的二进制文件(JPG/MP4/AVI等)本身就是由字节组成的,所以我们不需要像字符串那样用encode()转码,只需要以二进制模式读取文件,然后直接通过socket发送字节流就行;接收端则把收到的字节流写入文件即可。
不过要注意:如果是大文件(比如几百MB的视频),不能一次性把整个文件读入内存再发送,必须分块处理,否则会占用过多内存甚至导致程序崩溃。另外,为了避免socket粘包问题,最好先发送文件的大小,让接收端知道需要接收多少字节的数据。
具体实现示例
1. 发送/接收单张图片(小文件)
服务端(发送图片)
import socket import struct def send_image(): # 创建socket对象 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('localhost', 12345)) server_socket.listen(1) print("等待客户端连接...") conn, addr = server_socket.accept() print(f"已连接:{addr}") # 以二进制模式读取图片 with open("test.jpg", "rb") as f: image_data = f.read() # 先发送图片大小(用struct转成固定4字节的大端模式整数) conn.send(struct.pack('!I', len(image_data))) # 发送图片数据(sendall确保所有字节发送完成) conn.sendall(image_data) print("图片发送完成") conn.close() server_socket.close() if __name__ == "__main__": send_image()
客户端(接收图片)
import socket import struct def receive_image(): client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect(('localhost', 12345)) # 先接收图片大小(固定4字节) size_data = client_socket.recv(4) image_size = struct.unpack('!I', size_data)[0] # 循环接收图片数据,直到收满指定大小 received_data = b'' while len(received_data) < image_size: chunk = client_socket.recv(1024) if not chunk: break received_data += chunk # 以二进制模式写入文件 with open("received_test.jpg", "wb") as f: f.write(received_data) print("图片接收并保存完成") client_socket.close() if __name__ == "__main__": receive_image()
2. 发送/接收大视频文件(分块处理)
对于几百MB甚至GB级的视频,分块读写是必须的,避免内存溢出:
服务端(发送视频)
import socket import struct import os def send_large_file(file_path): server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('localhost', 12346)) server_socket.listen(1) print("等待客户端连接以接收视频...") conn, addr = server_socket.accept() print(f"已连接:{addr}") # 获取文件总大小 file_size = os.path.getsize(file_path) # 发送文件大小(用Q格式转成8字节大端模式整数,支持更大的文件) conn.send(struct.pack('!Q', file_size)) # 分块发送文件,每次读4KB with open(file_path, "rb") as f: while True: chunk = f.read(4096) if not chunk: break conn.sendall(chunk) print("视频发送完成") conn.close() server_socket.close() if __name__ == "__main__": send_large_file("test.mp4")
客户端(接收视频)
import socket import struct def receive_large_file(save_path): client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_socket.connect(('localhost', 12346)) # 接收文件大小(固定8字节) size_data = client_socket.recv(8) file_size = struct.unpack('!Q', size_data)[0] # 分块接收并写入文件,同时打印进度 received_size = 0 with open(save_path, "wb") as f: while received_size < file_size: chunk = client_socket.recv(4096) if not chunk: break f.write(chunk) received_size += len(chunk) print(f"已接收:{received_size/file_size:.2%}", end='\r') print("\n视频接收并保存完成") client_socket.close() if __name__ == "__main__": receive_large_file("received_test.mp4")
关键细节说明
- 二进制模式读写:打开文件时必须用
rb(读二进制)和wb(写二进制)模式,不能用默认的文本模式,否则会破坏二进制文件的结构。 sendall()的必要性:send()可能只发送部分字节,而sendall()会循环调用send()直到所有数据都发送完毕,传输文件时优先使用它。- 文件大小的传输:用
struct模块把整数转成固定长度的字节(比如!I对应4字节、!Q对应8字节),这样接收端能准确解析出文件总大小,避免粘包问题。 - 分块大小调整:分块大小可以根据实际网络情况调整,4KB-8KB是比较通用的选择,太大可能导致传输卡顿,太小则会增加IO次数。
内容的提问来源于stack exchange,提问作者janjanjan
相关产品推荐
相关产品推荐

