在 TCP/IP python 中添加编码和解码的位置

where to add encode and decode in TCP/IP python

我需要 send/receive 从一台笔记本电脑到另一台笔记本电脑的数据,我正在使用 python 3.6 并且这个版本的 python 似乎需要 .encode().decode() 函数,我使用的是 python 2.7 并且之前没有遇到任何问题,但是现在和 python 3.6 我必须使用这些函数,但我不确定我应该在哪里使用 .decode() 命令你会在我的服务器文件和我的客户端文件下面找到 服务器文件:

import socket
import threading
import os

def RetrFile(name, sock):
    filename = sock.recv(1024)
    filename.decode()
    if os.path.isfile(filename):
        sock.send("EXISTS " + str(os.path.getsize(filename)))
        userResponse = sock.recv(1024)
        if userResponse[:2] == 'OK':
            with open(filename, 'rb') as f:
                bytesToSend = f.read(1024)
                sock.send(bytesToSend)
                while bytesToSend != "":
                    bytesToSend = f.read(1024)
                    sock.send(bytesToSend)
    else:
        sock.send("ERR ")

    sock.close()

def MainServer():
    host = '172.16.1.2' #my IP adress
    port = 5000

    s = socket.socket()
    s.bind((host,port))

    s.listen(5)

    print ("Server Started.")
    while True:
        c, addr = s.accept()
        print ("client connected ip:<" + str(addr) + ">")
        t = threading.Thread(target=RetrFile, args=("RetrThread", c))
        t.start()

    s.close()

if __name__ == '__main__':
    MainServer()

这是我的客户文件:

import socket
def MainClient():
    host = '172.16.1.2'
    port = 5000

    s = socket.socket()
    s.connect((host, port))

    filename = input("Filename? -> ")
    byt=filename.encode()
    if byt != 'q':
        s.send(byt)
        data = s.recv(1024)
        if data[:6] == 'EXISTS':
            filesize = len(data[6:])
            message = input("File exists, " + str(filesize) +"Bytes, download? (Y/N)? -> ")
            if message == 'Y':
                s.send("OK")
                f = open('new_'+filename, 'wb')
                data = s.recv(1024)
                totalRecv = len(data)
                f.write(data)
                while totalRecv < filesize:
                    data = s.recv(1024)
                    totalRecv += len(data)
                    f.write(data)
                    print ("{0:.2f}".format((totalRecv/float(filesize))*100)+ "% Done")
                print ("Download Complete!")
                f.close()
        else:
            print ("File Does Not Exist!")

    s.close()    

if __name__ == '__main__':
    MainClient()

但是当我尝试 运行 它时,我在服务器端收到此错误:

sock.send("ERR ")
TypeError: a bytes-like object is required, not 'str'

感谢您的帮助。

你正在发明协议,所以你可以决定什么时候应该解码。一种选择是说基本协议(像 OK 和 ERR 这样的东西总是 ascii 并且可以作为字节进行管理)但是文件名和类似的东西是 utf-8 编码的字符串,需要解码。但是二进制文件呢?您要么需要将流的那部分定义为二进制,要么对内容进行一些 binary-to-ascii 编码。

但它的方式比这更复杂。 TCP是一种流协议。没有任何内容表明 recv 获得了另一方在单个调用中发送的所有内容,或者它以方便的 utf-8 字符边界结束。您不能只是 recv(1024) 并期望获得准确的消息边界。

所以,是时候彻底反思了:定义一个不同的协议。要遵循您现有的模式,您可以使用基于 header 的 ascii,而不是以新行结尾。 header 是一些命令或状态,后跟每个附加参数的字节大小,然后是换行符。后面直接写参数

你可以有这样的命令

STAT <sizeof utf-8 encoded file name>\n<utf-8-encoded-filename>
EXISTS ascii-encoded-size\n
DOWNLOAD <sizeof utf-8 encoded file name>\n<utf-8-encoded-filename>

以及从输入流中获取提示以了解下一步要做什么的解析器。例如,headers 不包含换行符但以换行符结尾,因此您可以有一个函数读取下一个 header:

def read_header(sock):
    hdr = []
    while True:
        c = sock.recv(1)
        if c == "\n":
            return ''.join(hdr).encode('ascii')

而 headers 告诉您管道中有多少数据,因此您可以为此设置一个 reader。

def read_chunk(sock, size, encoding=None):
    buf = io.bytesIO()
    while size:
        tmp = sock.recv(min(size, 4096))
        if not tmp:
            raise OSError("Unexpected end of stream")
        buf.write(tmp)
        size -= len(tmp)
    if encoding:
        return buf.getvalue().encode(encoding)
    else:
        return buf.getvalue()

读取流将是

while True:
    hdr = read_header(sock)
    # try commands
    m = re.match(b"STAT (\d+)", hdr)
    if m:
        stat_size = int(m.group(1))
        file_name = read_chunk(sock, stat_size, 'utf-8')
        # do the stat work...
        continue
    m = re.match(b"DOWNLOAD (\d+)", hdr)
    etc...