在TCP / IP python

时间:2018-05-06 00:05:08

标签: python tcp-ip

我需要从一台笔记本电脑向另一台笔记本电脑发送/接收数据,我正在使用python 3.6,看起来这个版本的python需要.encode().decode() 函数,我正在使用python 2.7而我之前没有遇到麻烦,但是现在和python 3.6我都要使用这些函数,但是我不确定我应该在哪里使用{{1命令您将在我的服务器文件和我的客户端文件下面找到 服务器文件:

.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()

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

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

但它的方式比那更复杂。 TCP是一种流协议。没有任何东西可以说recv获取对方在一次通话中发送的所有内容,或者它以方便的utf-8字符边界结束。您不能只是recv(1024)并希望得到准确的邮件边界。

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

你可以拥有像

这样的命令
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>

从输入流中获取提示以获知下一步操作的解析器。例如,标题不包含换行符,但以换行符结尾,因此您可以使用读取下一个标题的函数:

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

标题会告诉您管道中有多少数据,因此您可以拥有一个阅读器。

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...