每个UnicodeDecodeError套接字发送多个文件时出错:'utf-8'编解码器无法解码位置5的字节0x89

问题描述 投票:0回答:2

我有一个简单的客户端-服务器程序,在其中尝试发送包含2个文件[“ img1.jpg”,“ img2.jpg”]的Documents文件夹的内容。

正在运行:

服务器等待客户端连接并从中接收消息,但是如果消息是文本:files,则createExp()函数将接收要创建的新文件夹的名称以及将要发送的文件数量开始已收到。

使用该数据,我开始了一个for循环,必须根据用户向服务器指示的文件数来重复该循环。

循环到:

此循环具有接收客户端发送并随后保存在指定路径中的每个文件的数据的功能。

问题:

服务器正确接收到一小部分数据,但随后引发错误:

Traceback (most recent call last):
  File "C:\Users\Dell\Desktop\servidor_recv_archivo.py", line 53, in <module>
    if msgp.decode() == "files":
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x89 in position 5: invalid
start byte

server.py

import socket
import os

def bytes_to_int(b):
    result = 0
    for i in range(4):
        result += b[i]<<(i*8)
    return result

def makeExp(client):
    while True:
        FolderName = client.recv(1024).decode()
        NumberFiles = client.recv(1024).decode()

        print(FolderName,NumberFiles)
        if not os.path.exists(FolderName):
            os.mkdir(FolderName)

        for element in range(int(NumberFiles)):
            size = client.recv(4)
            size = bytes_to_int(size)
            current_size = 0
            buffer = b""
            while current_size < size:
                data = client.recv(1024)
                if not data:
                    break
                if len(data) + current_size > size:
                    data = data[:size-current_size]
                buffer += data
                current_size += len(data)
            with open(str(element),"wb") as f:
                f.write(buffer)
        break

ip = "192.168.8.8"
port = 5555
data = (ip,port)
listen = 2

server = socket.socket()
server.bind(data)
server.listen(listen)

client,direction = server.accept()

while True:
    try:
        msgp = client.recv(1024)

        print(msgp)
        client.sendall("Msg recv".encode())
        if msgp.decode() == "files":
            makeExp(client)
    except ConnectionResetError:
        print("{} ".format(direction))
        break

client.py

import socket
import os

def convert_to_bytes(length):
    result = bytearray()
    result.append(length&255)
    for i in range(3):
        length = length>>8
        result.append(length&255)
    return result

def makeFolder(client):
    rute = "C:/Users/AngelHp/Desktop/Documentos"
    FolderName = os.path.basename("C:/Users/AngelHp/Desktop/Documentos")
    NumberFiles = str(len(os.listdir("C:/Users/AngelHp/Desktop/Documentos")))

    client.sendall(FolderName.encode())
    client.sendall(NumberFiles.encode())

    for element in (os.listdir(rute)):
        length = os.path.getsize(rute+"/"+element)
        client.send(convert_to_bytes(length))
        with open(rute+"/"+element,"rb") as infile:
            d = infile.read(1024)
            while d:
                client.send(d)
                d = infile.read(1024)

ip = "192.168.8.8"
port = 5555

client = socket.socket()
client.connect((ip,port))

while True:
    msg = input("> ")
    if msg != "files": #Al oprimir el boton guarar en serv, lanzara la funcion crearExpServ
        client.sendall(msg.encode())
        reply = client.recv(1024).decode()
        print(reply)
    elif msg == "files":
        print("ok")
        makeFolder(client)

@ mark-已编辑

import socket
with socket.socket() as s:
    s.bind(('',8000))
    s.listen(1)
    with s.accept()[0] as c:
        chunks = []
        while True:
            chunk = c.recv(4096)
            if not chunk: break
            chunks.append(chunk)
    for i in range(2):
        with open('image{}.png'.format(str(i)),'wb') as f:
            f.write(b''.join(chunks))

cieent.py

import socket
import os

with socket.socket() as s:
    s.connect(('localhost',8000))
    for elemento in os.listdir("img"):
        print(elemento)
        with open("img"+"/"+elemento,'rb') as f:
            s.sendall(f.read())
python python-3.x sockets
2个回答
1
投票

TCP是一种流协议,没有消息边界的概念,因此,如果打印msgp,您将看到它收到的数据超出预期,可能是文件夹名称,文件数和部分二进制文件数据。由于该数据不是UTF-8编码的,因此会出现UnicodeDecodeError。

您必须定义一个协议并从套接字中缓冲数据,直到它满足该协议为止(例如,读取到换行符)。另请参见socket.makefile,其中将套接字包装在类似文件的对象中,因此您可以将其更像文件一样对待。存在.readline().read(n)之类的方法,因此您可以定义以下协议:

  1. 发送文件夹名称+换行符
  2. 发送文件数+换行符
  3. 发送文件名#1 +换行符
  4. 发送文件大小+换行符
  5. 发送精确字节的二进制数据。
  6. 对剩余文件重复3-5。

示例:


0
投票

问题是,您始终可以将文本编码为字节,例如"🙂".encode(),但不能始终将任意字节序列解码为文本。这是因为not all sequences of bytes are valid UTF-8文本。当您尝试对二进制数据进行解码以检查其是否等于"files"字符串时,如果Python检测到UTF-8文本标准不使用的字节序列,它将抛出异常。

# Will crash if msgp contains byte sequences that aren't defined in the UTF-8 standard.
if msgp.decode() == "files":

通过首先将文本转换为字节,然后比较字节本身**,我能够使您的代码部分正常工作*:

if msgp == "files".encode(): # comparing binary won't crash

*您还需要确保客户端实际上在"files"语句中将消息elif发送到服务器。为了进行测试,我通过在发送每条消息后增加延迟来解决消息边界不足的问题,但正如Mark Tolonen所建议的那样,引入消息边界协议会更好!

**此技术对于使用magic number区分消息/文件的协议(例如, PDF文件以“%PDF”开头。但是,请注意,某些Unicode字符具有multiple valid binary representations,因此在字节级别比较Unicode可能会导致问题。

© www.soinside.com 2019 - 2024. All rights reserved.