在客户端和服务器之间传输文件(套接字错误和属性错误)

时间:2017-11-22 13:48:14

标签: python sockets client-server file-transfer

我正在尝试通过TCP套接字发送和接收文件

有很多问题 1.当客户端连接到服务器时。服务器不“打印客户端已连接...”,但在使用该命令后打印 2.当我在服务器上使用'put'命令时发生错误socket.error:[Errno 107]传输端点未连接但文件映像已上传到服务器。
3.当我在客户端使用'get'命令时。我不能继续使用另一个命令 4.最后一个问题是客户端无法退出并从服务器列出文件。它显示了AttributeError:'module'对象没有属性'send'

服务器

import socket
import sys
import os
HOST = 'localhost'                 
PORT = 3820

socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
socket.bind((HOST, PORT))

socket.listen(1)
while (1):
    conn, addr = socket.accept()
    print 'Client connected ..'
    reqCommand = conn.recv(2048)
    print 'Client> %s' %(reqCommand)
    if (reqCommand == 'quit'):
        break

    #list file on server
    elif (reqCommand == 'lls'):
        start_path = os.listdir('.') # server directory
        for path,dirs,files in os.walk(start_path):
            for filename in files:
                print os.path.join(filename)

    else:
        string = reqCommand.split(' ', 1)   #in case of 'put' and 'get' method
        reqFile = string[1] 

        if (string[0] == 'put'):
            with open(reqFile, 'wb') as file_to_write:
                while True:
                    data = socket.recv(1024)
                    # print data
                    if not data:
                        break
                    # print data
                    file_to_write.write(data)
                    file_to_write.close()
                    break
            print 'Receive Successful'

        elif (string[0] == 'get'):
            with open(reqFile, 'rb') as file_to_send:
                for data in file_to_send:
                    conn.sendall(data)
            print 'Send Successful'

conn.close()

socket.close()

客户

import socket
import sys
import os

HOST = 'localhost'
PORT = 3820

def put(commandName):
    socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    socket1.connect((HOST, PORT))
    socket1.send(commandName)
    string = commandName.split(' ', 1)
    inputFile = string[1]
    with open(inputFile, 'rb') as file_to_send:
        for data in file_to_send:
            socket1.sendall(data)
    print 'PUT Successful'
    socket1.close()
    return

def get(commandName):
    socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    socket1.connect((HOST, PORT))
    socket1.send(commandName)
    string = commandName.split(' ', 1)
    inputFile = string[1]
    with open(inputFile, 'wb') as file_to_write:
        while True:
            data = socket1.recv(2048)
            # print data
            if not data:
                break
            # print data
            file_to_write.write(data)
    file_to_write.close()
    print 'GET Successful'
    socket1.close()
    return


msg = raw_input('Enter your name: ')
while(1):
    print 'Instructions'
    print '"put [filename]" to upload the file to the server '
    print '"get [filename]" to download the file from the server '
    print '"ls" to list all files in the client'
    print '"lls" to list all files in the server'
    print '"quit" to exit'
    sys.stdout.write('%s> ' % msg)
    inputCommand = sys.stdin.readline().strip()

    if (inputCommand == 'quit'):
        socket.send('quit')
        break

    #list file on client
    elif (inputCommand == 'ls'):
        start_path = os.listdir('.') # client directory
        print start_path

    #list file on server

    elif (inputCommand == 'lls'):
        socket.send('lls')

    else:
        string = inputCommand.split(' ', 1)
        if (string[0] == 'put'):
            put(inputCommand)
        elif (string[0] == 'get'):
            get(inputCommand)

socket.close()

1 个答案:

答案 0 :(得分:0)

除此之外,您需要添加"框架"你的传输协议。当您在流套接字上执行send时,数据将添加到最终将传递到另一端的缓冲区中。但是,该缓冲区的大小传输到另一侧。换句话说,假设您使用命令"put myfile.txt"发送初始段。然后从myfile.txt发送数据。现在因为你正在使用文件对象迭代器(for data in file_to_send),你实际上是一次发送一行(可以说,对于文件传输协议,读取和发送固定块更有意义但这会也工作)。我们假设myfile.txt的第一行是"The quick brown fox\n"

当服务器首次接收时,它可以接收"put ""put myfile.txt""put myfile.txtThe quick brown fox\n"put命令以及整个文件内容。这是因为流协议(TCP)不会为您维护消息边界

现在,在实践中,可能在第一次接收时只接收"put myfile.txt",但依靠它是非常不明智的,因为它依赖于在您无法控制的发送和接收系统上的各种因素的时间安排。

因此,有两种常见的处理方式:

  1. 在开头添加一个长度,用于描述命令的大小和任何命令参数(以便您知道要在流中的哪个位置开始传输实际文件数据)。 (大多数二进制文件传输协议都以这种方式工作。)

  2. 在命令末尾添加一些已知分隔符 - 例如'\n'。例如,HTTP以这种方式工作。

  3. 同样,您的接收方需要确保它正在准确读取每个点所需的数量,以便保留正在传输的文件的完整内容。

    这意味着您要么(a)要小心recv命令数据所需的字节数,然后单独处理文件内容,或者(b)recv一个初始块将数据放入缓冲区,然后准确地删除&#34;命令&#34;所需的内容,并确保其余内容稍后将作为文件数据处理。选项(b)通常可以通过在套接字顶部构建文件对象来实现(参见socket.makefile),然后使用文件对象的readline方法仅获取第一行。< / p>

    您的代码存在其他问题:

    1. socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)是一个非常糟糕的主意。您刚刚通过创建具有相同名称的变量隐藏了整个socket模块。例如,如果您尝试在后续行再次引用socket.AF_INET,则会收到例外AttributeError: '_socketobject' object has no attribute 'AF_INET'。您应该将变量命名为其他内容,例如socket1,就像在客户端那样。

    2. 在客户端,你遇到了相反的问题。您尝试使用套接字对象方法,但提供socket模块对象。 (这就是您获得AttributeError: 'module' object has no attribute 'send'的原因。)您需要重新组织代码,以便在sendput中调用已连接的套接字对象上的get方法。 socket.error: [Errno 107] Transport endpoint is not connected函数。

    3. 发生错误recv是因为您在侦听套接字上尝试conn,而不是socket.accept返回的已连接的套接字accept )。对于侦听套接字,唯一可以做的就是close个新连接(或sendall)。

    4. 您应该使用send而不是send来确保每个字节都被发送。一般来说,所有数据也会随while True:一起发送,但有些情况并非如此。

    5. 服务器中的文件接收循环以breaks开头,但始终为lls。因此它只接收第一个数据块(最多1024个字节)。如果文件大于那个,你肯定会截断它。

    6. 您的服务器文件列表功能({{1}}命令)未将其输出发送回客户端,它只将其打印到服务器端的控制台。 (不要忘记,你需要在你发回的文件名之间提供某种分隔符,否则它们最终会连成一个大字符串。)