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

Python中使用socket.recv()接收TCP/IP图像时出现损坏问题

问题根源:TCP字节流的边界问题

你的核心问题在于TCP是无消息边界的字节流协议,recv()方法并不会一次性返回你请求的所有字节——它只会返回当前缓冲区中可用的数据,可能小于你指定的长度。而你直接用单次recv()来获取固定长度的头部(4字节大小、2字节计数器)和图像数据,很可能只拿到了部分数据,导致后续解析完全错乱,最终图像损坏。

makefile()方法之所以能正常工作,是因为它的read()方法内部会自动循环读取,直到凑够你指定的字节数,帮你处理了流的边界问题;而直接用recv()时,你需要自己实现这个逻辑。

修复方案:实现一个「固定长度接收」的辅助函数

我们可以写一个简单的函数,确保每次都能接收到指定长度的数据,而不是依赖单次recv()的返回结果:

def recv_all(sock, expected_length):
    """从socket接收指定长度的字节数据,直到全部接收完毕"""
    received_data = b''
    while len(received_data) < expected_length:
        chunk = sock.recv(expected_length - len(received_data))
        if not chunk:
            # 连接已关闭,返回已接收的部分(或抛出异常)
            return received_data
        received_data += chunk
    return received_data

然后修改你的服务端代码,把所有需要固定长度接收的地方替换成这个函数:

import socket
import struct
import io
import cv2
import numpy as np

def recv_all(sock, expected_length):
    received_data = b''
    while len(received_data) < expected_length:
        chunk = sock.recv(expected_length - len(received_data))
        if not chunk:
            return received_data
        received_data += chunk
    return received_data

server_socket= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server_socket.bind(('192.168.1.106',9000))
server_socket.listen(1)
print('Server is ready')
(client_conn,client_addr)= server_socket.accept()
image_stream= io.BytesIO()
try:
    while True:
        # 接收4字节的图像大小
        size_data = recv_all(client_conn, 4)
        if not size_data:
            break
        data_size=struct.unpack('<L', size_data)[0]
        
        # 接收2字节的计数器
        counter_data = recv_all(client_conn, 2)
        if not counter_data:
            break
        counter=struct.unpack('<h', counter_data)[0]
        
        print('Image Count = ',counter)
        print('Image size = ',data_size)
        
        if not data_size:
            break
        
        # 接收完整的图像数据
        image_data = recv_all(client_conn, data_size)
        image_stream.write(image_data)
        
        image= np.frombuffer(image_stream.getvalue(), dtype=np.uint8)
        img=cv2.imdecode(image, -1)
        cv2.imwrite((str(counter)+'.jpeg'),img)
        
        image_stream.seek(0)
        image_stream.truncate()
finally:
    server_socket.close()
    print('Server Closed.')

关键细节解释

  1. 为什么单次recv不可靠?
    TCP会把数据拆分成多个数据包传输,或者把多个小消息合并成一个数据包发送(粘包)。recv(n)最多返回n字节,但可能只返回当前到达的部分数据——比如你请求4字节,可能先收到2字节,剩下的2字节后续才到达。如果不循环接收,就会用不完整的头部数据去解析,导致后续所有数据的位置都错乱,图像自然损坏。

  2. sendall()和recv()的区别
    sendall()确实会保证把所有数据发送出去(如果连接没断),但它只是负责把数据交给TCP栈,TCP会处理分片和重传;而recv()只负责从本地缓冲区拿当前可用的数据,不会自动等待后续数据,所以必须由你自己处理「凑够长度」的逻辑。

  3. 关于np.fromstring
    注意np.fromstring()已经被弃用了,建议改用np.frombuffer(),我在修复代码里已经帮你替换了,避免后续版本的兼容性问题。

这样修改后,你就能确保每次都接收到完整的头部和图像数据,图像应该就能正常保存了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:33:42