Transferring file between client and server (Socket error and Attribute error)

只愿长相守 提交于 2019-12-11 14:39:35

问题


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

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

回答1:


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"

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.

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

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

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.

Other issues with your code:

  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.

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

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

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

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

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



来源:https://stackoverflow.com/questions/47436349/transferring-file-between-client-and-server-socket-error-and-attribute-error

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!