在客户端和服务器之间传输文件(套接字错误和属性错误) [英] Transferring file between client and server (Socket error and Attribute error)

查看:101
本文介绍了在客户端和服务器之间传输文件(套接字错误和属性错误)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I’m trying to send and receive file through TCP socket

有很多问题

1.当客户端连接到服务器。服务器未打印连接的客户端..,但使用命令后将进行打印。

2.当我在服务器上使用 put命令时,发生错误socket.error:[Errno 107]传输端点未连接,但文件图像已上传到服务器。

3.在客户端上使用 get命令时。我无法继续使用其他命令。

4.最后一个问题是客户端无法退出并从服务器列出文件。它显示AttributeError:'module'对象没有属性'send'

There are a lot of problems
1. When the client connects to the server. The server does not "print Client connected .." but it prints after using the command.
2. When I use the ‘put’ command at the server occur an error socket.error: [Errno 107] Transport endpoint is not connected but the file image is already uploaded to the server.
3. When I use the ‘get’ command at the client. I can’t continue to use another command.
4. The last problem is the client can’t quit and list file from the server. It shows AttributeError: 'module' object has no attribute 'send'

Server

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

Client

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


推荐答案

除其他外,您需要在传输协议中添加成帧。当您在流套接字上执行 send 时,数据将被添加到缓冲区中,缓冲区最终将被传递到另一端。但是,该缓冲区的大小传输到另一侧。换句话说,假设您使用命令 put myfile.txt 发送初始段。然后,您从 myfile.txt 发送数据。现在,因为您正在使用文件对象迭代器(用于file_to_send 中的数据),所以实际上您一次要向其发送一行(可以说,对于文件传输协议,它将读取和发送固定的块更有意义,但这也可以工作)。假设 myfile.txt 的第一行是快速棕色狐狸

Among other things, you need to add "framing" to your transfer protocol. When you do a send on a stream socket, the data gets added to a buffer that will eventually be delivered to the other side. However, the size of that buffer is not transmitted to the other side. In other words, say you send an initial segment with the command "put myfile.txt". Then you send the data from myfile.txt. Now because you are using the file object iterator (for data in file_to_send), you are actually sending it a line at a time (arguably, for a file transfer protocol, it would make more sense to read and send fixed chunks but this would work too). Let's assume the first line of myfile.txt is "The quick brown fox\n"

服务器首次接收时,它可能会接收 put put myfile.txt put myfile.txt快速的棕色狐狸 put 命令加上整个文件的内容。那是因为流协议(TCP)不为您维护消息边界

When the server does its first receive, it could receive "put " or "put myfile.txt" or "put myfile.txtThe quick brown fox\n" or the put command plus the entire file contents. That's because the stream protocol (TCP) does not maintain message boundaries for you.

现在,实际上,您可以 >在第一次接收中仅接收 put myfile.txt ,但是指望这样做是非常不明智的,因为这取决于两者上各种因素的时机发送和接收系统无法控制的系统。

Now in practice, you may be receiving only the "put myfile.txt" in the first receive, but it's very unwise to count on that because it's dependent on the timing of all sorts of factors on both sending and receiving systems that are outside your control.

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

So, there are two common ways of handling this:


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

  1. Add a length at the beginning that delineates the size of the command and any command argument (so that you know where in the stream the actual file data to be transferred begins). (Most binary file transfer protocols work this way.)

在命令末尾添加一些已知的定界符-例如, '\n'。例如,HTTP就是这样工作的。

Add some known delimiter at the end of your command -- for example, '\n'. HTTP, for example, works this way.

同样,您的接收方也需要确保它正在读取每个点所需的数量,以保留要传输的文件的全部内容。

And likewise your receiving side needs to ensure that it is reading exactly the amount needed at each point in order to preserve the full content of the file being transferred.

这意味着您要么(a)小心地 c 精确地存储命令数据所需的字节数,然后分别处理文件内容,或(b)将初始数据块 recv 放入缓冲区,然后精确分配命令所需的内容,并确保其余的将在以后作为文件数据处理。选项(b)通常可以通过在套接字顶部构建文件对象来实现(请参见 socket.makefile ),然后使用文件对象的 readline 方法仅获取第一行。

That means you either (a) are careful to recv exactly the number of bytes you need for the command data, then separately process the file content, or (b) recv an initial chunk of data into a buffer, then carve off exactly what you need for the "command", and ensure the rest will be processed later as file data. Option (b) can often be accomplished by building a file object on top of the socket (see socket.makefile), then using the file object's readline method to obtain only the first line.

代码的其他问题:


  1. socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)行是一个非常糟糕的主意。通过创建具有相同名称的变量,您刚刚隐藏了整个 socket 模块。例如,如果您尝试在下一行再次引用 socket.AF_INET ,则会出现异常 AttributeError:'_socketobject'对象具有没有属性 AF_INET 。您应该使用其他名称来命名变量,就像在客户端一样使用 socket1

  1. The line socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) is a very bad idea. You have just hidden the entire socket module by creating a variable with the same name. For example, if you attempted to refer to socket.AF_INET again on the subsequent line, you'd get the exception AttributeError: '_socketobject' object has no attribute 'AF_INET'. You should name the variable something else, say socket1 as you did on the client side.

在在客户端,您遇到了相反的问题。您正在尝试使用套接字 object 方法,但改为提供 socket 模块对象。 (这就是为什么出现 AttributeError:'module'对象没有属性'send'的原因。)您需要重新组织代码,以便调用 send就像在 put get 函数中所做的一样,在已连接的套接字对象上使用方法。

On the client side, you have the opposite problem. You're attempting to use a socket object method, but providing the socket module object instead. (That's why you get AttributeError: 'module' object has no attribute 'send'.) You need to reorganize your code so that you call the send method on a connected socket object as you are doing in the put and get functions.

错误 socket.error:[Errno 107]传输端点未连接的原因是尝试在侦听套接字上尝试 recv ,而不在已连接的套接字( conn 上,它由<$ c $返回c> socket.accept )。您可以使用监听套接字执行的唯一操作是接受新连接(或关闭)。

The error socket.error: [Errno 107] Transport endpoint is not connected occurs because you are attempting to recv on the listening socket, not the connected one (conn -- which is returned by socket.accept). The only thing you can do with a listening socket, is accept new connections (or close).

您应该使用 sendall 而不是 send 来确保每个字节都被发送。通常,所有数据也会通过发送发送,但是在某些极端情况下,这种情况不会发生。

You should be using sendall instead of send to ensure that every byte gets sent. Generally, all the data will get sent with send too, but there are corner cases where that doesn't happen.

服务器中的文件接收循环以开始,而True:开始,但随后总是中断。因此,它将仅接收第一个数据块(最大1024字节)。如果文件大于该文件,您肯定会最终将其截断。

Your file-receiving loop in the server begins with while True: but then always breaks. Hence it will only receive the first chunk of data (up to 1024 bytes). If the file is larger than that, you will certainly end up truncating it.

您的服务器文件列表功能( lls 命令)不会将其输出发送回客户端,而只是将其输出到服务器端的控制台。 (并且不要忘记,您需要在发送回的文件名之间提供某种分隔符,否则它们最终将全部串联成一个大字符串。)

Your server file listing function (lls command) is not sending its output back to the client, it's only printing it to the server side's console. (And don't forget, you will need to provide a delimiter of some kind between the file names you send back or they will end up all concatenated into a single big string.)

这篇关于在客户端和服务器之间传输文件(套接字错误和属性错误)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆