问题
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
Client.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
Server.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()
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)
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
.
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')
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)
回答1:
Following on from the comments I made a couple changes as below:
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
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.
Here's the client:
#!/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)
Here's the server:
#!/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()
Keywords: TCP socket, stream, stream-based, streams-based, message, message-based, framing protocol, framed, htonl, network order, packet, prime.
来源:https://stackoverflow.com/questions/61048661/python-socket-error-when-sending-an-image-cant-decode-byte-unexpected-eof