发送图像时出现 Python 套接字错误。无法解码字节/意外 EOF
Posted
技术标签:
【中文标题】发送图像时出现 Python 套接字错误。无法解码字节/意外 EOF【英文标题】:Python socket error when sending an image.Can't decode byte / Unexpected EOF 【发布时间】:2020-04-05 19:57:18 【问题描述】:我正在使用 python 套接字将图像从笔记本电脑的相机(客户端)传输到我的树莓派(服务器)。事情是这样的,我连接到套接字,然后我的客户端等待信号以便拍照。当发送信号(来自我的服务器)时,我的客户端拍照并按顺序发送以下数据;首先是代表图片大小的数字的长度(例如,对于 10000 字节,数字是 5,对于 100000 字节,数字是 6,依此类推),然后发送实际大小,最后以字节串形式发送照片。该过程无限次重复
客户端.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 秒或更长时间)
示例运行 1(服务器)
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(服务器)
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
我还在我的服务器循环中插入了一个打印(照片)命令,以查看发生了什么,这就是结果(参见最后一行与其他行的比较)
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)
【问题讨论】:
你是否使用netstat
命令检查了它们之间的连接?
连接似乎很好,虽然我的路由器有点慢。
根据错误似乎发送一个字节都没有成功。检查它们之间的连接并增加连接之间的超时时间。
请再次阅读我的问题并查看我上传的示例运行。字节被接收了一段时间(甚至制作完整的图像),但在一些循环后突然停止。
问题出在data += sock.receive(size)
行中,您不应继续请求size
字节。您应该只请求总消息中仍然未完成的内容,否则您将获取下一条消息的标题。因此,创建一个bytesRemaining
变量并减去每次阅读时收到的任何内容,然后只请求剩余的内容。
【参考方案1】:
从 cmets 开始,我做了一些更改,如下所示:
与其将 JPEG 写入磁盘,然后计算其大小,然后再次将其编码到内存并转换为字符串并发送它,我只是将 JPEG 编码到内存缓冲区中,获取其大小并发送它
我没有发送一个字符串来表示第二个字符串中的字节数来表示视频帧中的字节数,而是将字节数作为 4 字节网络顺序整数发送。它让生活更轻松
它似乎在我的机器上运行得非常可靠。
这是客户:
#!/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、网络顺序、数据包、素数。
【讨论】:
以上是关于发送图像时出现 Python 套接字错误。无法解码字节/意外 EOF的主要内容,如果未能解决你的问题,请参考以下文章
简单地将图像发送到 icloud 时出现“无法完成操作。不允许操作”错误
在 Python 中使用来自 Gravitee 的公钥解码 JWT 令牌时出现问题
尝试解码 Jwt 令牌时出现“尝试解码 Jwt 时发生错误:无法检索远程 JWK 集:”错误
安装 Laravel 时出现 Composer 错误“无法解码响应:zlib_decode():数据错误”