通过socket.recv()

时间:2018-01-07 15:00:56

标签: image sockets tcp raspberry-pi3 python-3.6

我试图将图像从raspberry pi流式传输到我的Windows笔记本电脑(python 3.6.3)并接收损坏的图像(更多细节请遵循代码)。以下是我的代码。

客户方:

import picamera
import socket
import struct
import time
import io

stream_client= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
stream_client.connect(('192.168.1.106',9000))
print('connected')
camera = picamera.PiCamera()
camera.resolution=(320,240)
camera.color_effects=(128,128)
camera.framerate=18
time.sleep(2)
stream = io.BytesIO()
count=0
start=time.time()

try:
    for i in camera.capture_continuous(stream,'jpeg',use_video_port=True):
        count+=1
        stream_client.sendall(struct.pack('<L',stream.tell()))
        stream_client.sendall(struct.pack('<h',count))
        stream_client.sendall(stream.getvalue())
        stream.seek(0)
        stream.truncate()
        if(time.time()-start>2):
            break
finally:
    stream_client.sendall(struct.pack('<L',0))
    stream_client.close()
    camera.close()
    print('connection closed')

保存损坏图像的服务器端代码:

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

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:
        data_size=struct.unpack('<L',client_conn.recv(4))[0]
        counter=struct.unpack('<h',client_conn.recv(2))[0]
        print('Image Count = ',counter)
        print('Image size = ',data_size)
        if not data_size:
            break
        image_stream.write(client_conn.recv(data_size))
        image= np.fromstring(image_stream.getvalue(),dtype=np.uint8)
        img=cv2.imdecode(image, -1)
        print(image) # just wanted to see how the image data looked 
        cv2.imwrite((str(counter)+'.jpeg'),img)
        image_stream.seek(0)
        image_stream.truncate()



finally:
    server_socket.close()
    print('Exception Detected ! Server Closed.')

上述服务器代码可以保存大部分损坏的图像和垃圾图像,偶尔还会保存正确的图像。我尝试增加和减少缓冲区大小,但我认为这没有意义,它也没有帮助(实际上它使问题恶化)。
我还使用makefile()方法实现了服务器代码,并且我收到的图像是正确的。

我想的是这个问题存在,因为我没有缓冲数据并直接将数据提供给流。我觉得这是正确工作的服务器代码和不正确的服务器代码之间的唯一区别。

我意识到客户端中的sendall()方法会自动发送丢失(或损坏?)的数据,我觉得recv()方法不会自动处理这种重新传输?这也可能是问题的原因吗?或者这里发生了什么完全不同的事情?

无论如何,我想通过recv()方法实现图像传输代码,因为我相信它会加深我对套接字编程的理解。有人能告诉我我做错了什么吗?任何帮助表示赞赏。

P.S:这是我在Stackoverflow的第一个问题,所以我尽可能地详细说明了我的问题和想法。如果你觉得我应该减少/增加细节,请告诉我。欢呼:)

1 个答案:

答案 0 :(得分:0)

    image_stream.write(client_conn.recv(data_size))

您希望recv返回所有data_size个字节。但是,size参数仅提供它将读取的最大字节数。来自the documentation

  

socket.recv(bufsize [,flags])
...一次接收的最大数据量由bufsize指定。

您必须检查返回值实际接收的数据量,然后再次调用recv,直到您拥有所有数据,例如:

while data_size>0:
    buf = client_conn.recv(data_size)
    if buf == '':
        raise Exception("unexpected eof")
    data_size -= len(buf)
    image_stream.write(buf)