发送图像时出现 Python 套接字错误.无法解码字节/意外的 EOF [英] Python socket error when sending an image.Can't decode byte / Unexpected EOF

查看:46
本文介绍了发送图像时出现 Python 套接字错误.无法解码字节/意外的 EOF的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 python 套接字将图像从我的笔记本电脑的相机(客户端)传输到我的树莓派(服务器).这是它的过程,我连接到套接字,然后我的客户端等待信号以拍照.当信号(来自我的服务器)发送时,我的客户端拍照并按顺序发送以下数据;首先是表示图片大小的数字的长度(例如,对于 10000 字节,数字是 5,对于 100000 字节,数字是 6,依此类推),然后发送实际大小,最后将照片作为字节字符串发送.该过程无限次重复

I am using a python socket to transfer images from my Laptop's camera (Client) to my raspberry pi (Server). Here is how it goes, I connect to the socket and then my client waits for a signal in order to take a photo. When the signal (from my server) is sent, my client takes a photo and sends the following data in order; First the length of the number representing the size of the picture (e.g. for 10000 bytes the number is 5, for 100000 bytes the number is 6 and so on), then sends the actual size and finally sends the photo as a byte string. The proccess is repeated infinite times

客户端.py

import cv2
import socket
import os

def send_msg(s, msg):
    s.sendall(msg)


s = socket.socket()
port = 24999
ip = '192.168.1.3'
s.connect((ip, port))

video = cv2.VideoCapture(0)

s.recv(1) #Wait until data is sent
while True:
    _, img = video.read()
    img = cv2.resize(img, (0,0), fx = 0.4, fy = 0.4) #reduce image size
    cv2.imwrite("test.jpg", img) #save img
    size = os.path.getsize("test.jpg") #get image size
    img_str = cv2.imencode('.jpg', img)[1].tostring() #convert to bytes string
    sizenum = str(len(str(size))) #how many digits the image size contains
    send_msg(s, sizenum.encode())
    send_msg(s, str(size).encode('utf-8')) #send actual image size
    send_msg(s, img_str) #finally send the image
    s.recv(1) #Wait until data is sent

服务器.py

import socket
import ast

def send_msg(client, msg):
    client.sendall(msg+b'\r\n')

def recvall(sock, n):
    # Helper function to recv n bytes or return None if EOF is hit
    data = bytearray()
    while len(data) < n:
        packet = sock.recv(n - len(data))
        if not packet:
            return None
        data.extend(packet)
    return data

def take_photo(cl):
    #RECEIVE LENGTH OF SIZE
    while True:
        lensize = cl.recv(1)
        lensize = lensize.decode('utf-8')
        if lensize != "":
            break
    print("Size is a",lensize,"-digit number")
    lensize = lensize.replace("\n","").replace(" ","").replace("\r","")
    #RECEIVE SIZE
    size_pic = recvall(cl,ast.literal_eval(lensize)).decode('utf-8')
    size_pic = size_pic.replace("\n","").replace(" ","").replace("\r","")
    print("Exact size is",size_pic)
    #RECEIVE PHOTO
    return lensize,size_pic,bytearray(recvall(cl,ast.literal_eval(size_pic)))

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("192.168.1.3",24999))
s.listen(1)

while True:
    cl_image, addr = s.accept()
    break

while True:
    try:
        send_msg(cl_image, b"1") #Send signal
        size1, size2, photo = take_photo(cl_image)
        print(photo)

    except KeyboardInterrupt:
        print("error")
        s.close()

问题运行服务器时,在运行几秒钟后出现问题(有时它在抛出任何异常之前只运行了 1 秒,有时它运行了 5 秒或更长时间)

The problem When running the server a problem occurs after some seconds of running (sometimes it runs just 1 second before throwing any exception and sometimes it runs 5 or more seconds)

示例运行 1(服务器)

Example run 1 (server)

Size is a 5 -digit number
Exact size is 21263
7294 from 21263 Not all bytes were read
18974 from 21263 Not all bytes were read
Size is a 5 -digit number
Exact size is 21226
2915 from 21226 Not all bytes were read
4375 from 21226 Not all bytes were read
11675 from 21226 Not all bytes were read
18975 from 21226 Not all bytes were read
Size is a 5 -digit number
Exact size is 21412
2915 from 21412 Not all bytes were read
7295 from 21412 Not all bytes were read
14595 from 21412 Not all bytes were read
Size is a . -digit number
Traceback (most recent call last):
  File "sending_test.py", line 46, in <module>
    size1, size2, photo = take_photo(cl_image)
  File "sending_test.py", line 27, in take_photo
    size_pic = recvall(cl,ast.literal_eval(lensize)).decode('utf-8')
  File "/usr/lib/python3.7/ast.py", line 46, in literal_eval
    node_or_string = parse(node_or_string, mode='eval')
  File "/usr/lib/python3.7/ast.py", line 35, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "<unknown>", line 1
    .

示例运行 2(服务器)

Example run 2 (server)

Size is a 5 -digit number
Exact size is 20653
7294 from 20653 Not all bytes were read
14594 from 20653 Not all bytes were read
18974 from 20653 Not all bytes were read
Size is a 5 -digit number
Exact size is 20595
2915 from 20595 Not all bytes were read
8755 from 20595 Not all bytes were read
10215 from 20595 Not all bytes were read
18975 from 20595 Not all bytes were read
Traceback (most recent call last):
  File "sending_test.py", line 46, in <module>
    size1, size2, photo = take_photo(cl_image)
  File "sending_test.py", line 21, in take_photo
    lensize = lensize.decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte

我还在服务器的循环中插入了一个打印(照片)命令以查看发生了什么,这就是结果(与其他人相比,请参阅最后一行)

I also inserted a print(photo) command in my server's loop to see whats happening and thats the result (See last line compared to others)

Size is a 3 -digit number
Exact size is 828
bytearray(b'\xff\xd8\xff.......\xff\xd9')
Size is a 3 -digit number
Exact size is 831
bytearray(b'\xff\xd8\xff......\xff\xd9')
Size is a 3 -digit number
Exact size is 831
bytearray(b'\xff\xd8\xff.......\xff\xd9383')

这意味着它读取了它应该有的(3)个字节(因为 3 表示大小长度,83 来自图像大小,类似于 (83x)

Which means that it reads some (3) more bytes that it should have (as 3 indicates the size length and 83 comes from the image size, which is something like (83x)

推荐答案

根据评论,我做了以下几处更改:

Following on from the comments I made a couple changes as below:

  • 与其将 JPEG 写入磁盘,然后计算出其大小,然后再将其编码到内存中并转换为字符串并发送,我只是将 JPEG 编码到内存缓冲区中,获取其大小并发送它

  • Rather than write the JPEG to disk and then work out its size and then encode it again to memory and convert to a string and send it, I just encode the JPEG into a memory buffer, get its size and send it

我没有发送一个字符串来表示第二个字符串中的字节数来表示视频帧中的字节数,而是将字节数作为一个 4 字节的网络顺序整数发送.它让生活更轻松

Rather than send a string to say the number of bytes in a second string to say the number of bytes in the video frame, I just send the number of bytes as a 4-byte network-order integer. It makes life much easier

它似乎在我的机器上工作得非常可靠.

It seems to work very reliably on my machine.

这是客户:

#!/usr/bin/env python3

import cv2
import socket
import os
import struct

ip, port = '192.168.0.8', 24999
s = socket.socket()
s.connect((ip, port))

# Start video reader
video = cv2.VideoCapture(0)

while True:
    # Wait till data requested, as indicated by receipt of single byte
    s.recv(1)
    print('CLIENT: Image requested')

    # Read a frame of video and reduce size
    _, img = video.read()
    img = cv2.resize(img, (0,0), fx = 0.4, fy = 0.4)

    # JPEG-encode into memory buffer and get size
    _, buffer = cv2.imencode('.jpg', img)
    nBytes = buffer.size
    print(f'CLIENT: nBytes={nBytes}')

    # Send 4-byte network order frame size and image
    hdr = struct.pack('!i',nBytes)
    s.sendall(hdr)
    s.sendall(buffer)

这是服务器:

#!/usr/bin/env python3

import time
import socket
import struct

def recvall(sock, n):
    # Helper function to recv n bytes or return None if EOF is hit
    data = bytearray()
    while len(data) < n:
        packet = sock.recv(n - len(data))
        if not packet:
            return None
        data.extend(packet)
    return data

def take_photo(cl):
    # Get header with number of bytes
    header = cl.recv(4)
    nBytes = struct.unpack('!i',header)[0]

    # Receive actual image
    img = recvall(cl, nBytes)
    return img

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("192.168.0.8",24999))
s.listen(1)

while True:
    cl_image, addr = s.accept()
    break

while True:
    try:
        # Request image by sending a single byte
        cl_image.sendall(b'1')
        photo = take_photo(cl_image)
        time.sleep(1)
        print(f'SERVER: photo received, {len(photo)} bytes')

    except KeyboardInterrupt:
        print("error")
        s.close()

关键字:TCP 套接字、流、基于流、基于流、消息、基于消息、成帧协议、成帧、htonl、网络顺序、数据包、素数.

Keywords: TCP socket, stream, stream-based, streams-based, message, message-based, framing protocol, framed, htonl, network order, packet, prime.

这篇关于发送图像时出现 Python 套接字错误.无法解码字节/意外的 EOF的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆