如何在Python中完成套接字文件传输?

时间:2014-11-29 20:35:50

标签: python sockets tcp file-transfer

我有一个客户端和一个服务器,我需要使用套接字传输一些文件。我可以发送小消息,但是当我尝试发送文件时,问题就开始了......

client.py:

from socket import *
from threading import Thread
import sys
import hashlib

class Client(object):

    ASK_LIST_FILES    = "#001" # 001 is the requisition code to list 
                               # all the files
    ASK_SPECIFIC_FILE = "#002" # 002 is the requisition code to a 
                               # specific file
    SEND_FILE         = "#003" # 003 is the requisition code to send one 
                               # file
    AUTHENTICATION    = "#004" # 004 is the requisition code to user
                               # authentication

    listOfFiles = []

    def __init__(self):
        try:
            self.clientSocket = socket(AF_INET, SOCK_STREAM)
        except (error):
            print("Failed to create a Socket.")
            sys.exit()


    def connect(self, addr):
        try:
            self.clientSocket.connect(addr)
        except (error):
            print("Failed to connect.")
            sys.exit()

        print(self.clientSocket.recv(1024).decode())

    def closeConnection(self):
        self.clientSocket.close()

    def _askFileList(self):
        try:
            data = Client.ASK_LIST_FILES
            self.clientSocket.sendall(data.encode())
            # self._recvFileList()
        except (error):
            print("Failed asking for the list of files.")
            self.closeConnection()
            sys.exit()

        thread = Thread(target = self._recvFileList)
        thread.start()

    def _recvFileList(self):
        print("Waiting for the list...")
        self.listOfFiles = []
        while len(self.listOfFiles) == 0:
            data = self.clientSocket.recv(1024).decode()
            if (data):
                self.listOfFiles = data.split(',')
                if(len(self.listOfFiles) > 0):
                    print (self.listOfFiles)

    def _askForFile(self, fileIndex):

        fileIndex = fileIndex - 1

        try:
            data = Client.ASK_SPECIFIC_FILE + "#" + str(fileIndex)
            self.clientSocket.sendall(data.encode())
        except(error):
            print("Failed to ask for an specific file.")
            self.closeConnection()
            sys.exit()

        self._downloadFile(fileIndex)

    def _downloadFile(self, fileIndex):
        print("Starting receiving file")
        f = open("_" + self.listOfFiles[fileIndex], "wb+")
        read = self.clientSocket.recv(1024)
        # print(read)
        # f.close
        while len(read) > 0:
            print(read)
            f.write(read)
            f.flush()
            read = self.clientSocket.recv(1024)
        f.flush()
        f.close()
        self.closeConnection()

server.py

from socket import *
from threading import Thread
import sys
import glob

class Server(object):

    def __init__(self):
        try:
            self.serverSocket = socket(AF_INET, SOCK_STREAM)
        except (error):
            print("Failed to create a Socket.")
            sys.exit()

    def connect(self, addr):
        try:
            self.serverSocket.bind(addr)
        except (error):
            print ("Failed on binding.")
            sys.exit()

    def closeConnection(self):
        self.serverSocket.close()

    def waitClients(self, num):
        while True:
            print("Waiting for clients...")
            self.serverSocket.listen(num)
            conn, addr = self.serverSocket.accept()
            print("New client found...")
            thread = Thread(target = self.clientThread, args = (conn,))
            thread.start()

    def clientThread(self, conn):
        WELCOME_MSG = "Welcome to the server"
        conn.send(WELCOME_MSG.encode())
        while True:
            data = conn.recv(2024).decode()
            if(data):
                # print(data)
                # reply = 'OK: ' + data
                # conn.sendall(reply.encode())
                if(data == "#001"):
                    listOfFiles = self.getFileList()
                    strListOfFiles = ','.join(listOfFiles)
                    self._sendFileList(strListOfFiles, conn)
                else:
                    dataCode = data.split('#')
                    print(dataCode)
                    if(dataCode[1] == "002"):
                        print("Asking for file")
                        self._sendFile(int(dataCode[2]), conn)
                    if(dataCode[1] == "003"):
                        print("Pedido de login")
                        if self._authentication(dataCode[2]):
                            conn.send("OK".encode())
                            # self._recvFile(conn)
                        else:
                            conn.send("FAILED".encode())



    def _sendFile(self, fileIndex, conn):
        listOfFiles = self.getFileList()
        print(fileIndex)
        print(listOfFiles[fileIndex])
        f = open(listOfFiles[fileIndex], "rb")
        read = f.read(1024)
        while len(read) > 0:
            conn.send(read)
            read = f.read(1024)          
        f.close()

    def _sendFileList(self, strList, conn):
        try:
            conn.sendall(strList.encode())
        except (error):
            print("Failed to send list of files.")

    def getFileList(self):
        return glob.glob("files/*")

当我尝试从我的服务器获取文件时,我可以传输所有内容,但连接永远不会结束。我的代码怎么了?

1 个答案:

答案 0 :(得分:0)

首先,您在这里使用TCP执行最常见的错误:假设在单个send()中发送的所有数据将在单个recv()中相同。这对于TCP来说是不真实的,因为它是 octet 流,而不是消息流。您的代码仅在理想(实验室)条件下工作,并且在现实世界中可能会神秘地失败。您应该在TCP流中明确地发明消息边界,或者切换例如到SCTP。后者现在几乎可以在任何地方使用,并通过网络连接保持消息边界。

第二个错误与第一个错误直接相关。发送文件时,您不能提供文件已完成的任何明确标记。所以,客户永远等待。您可能尝试关闭服务器连接以显示文件已完成,但在这种情况下,客户端无法区分真实文件结束和连接丢失;此外,连接不能再用于其他命令。您可以选择以下方法之一:

  • 以文件内容的长度作为前缀。在这种情况下,客户端将知道该文件应接收多少字节。
  • 将文件内容作为块序列发送,在每个块前面加上其长度(仅用于TCP),并标记该块是否为最后一个(对于两个传输)。或者,特殊标记" EOF"没有数据就可以发送。

同样,控制消息及其响应应提供长度前缀或终结符,这些消息不能出现在此类消息中。

完成开发后,你会看到FTP和HTTP;两者都解决了我在这里描述的所有问题,但主要有不同的方式。