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

标签 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/*")

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

最佳答案

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

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

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

  • 类似地,控制消息及其响应应带有长度前缀或不能出现在此类消息内的终止符。

    完成此开发后,您将了解FTP和HTTP。两者都解决了我在这里描述的所有问题,但主要以不同的方式。

    关于python - 如何在Python中完成套接字文件传输?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27206838/

    相关文章:

    http - 当我们有 TCP/IP 时,为什么 HTTP 操作需要是幂等的?

    c - TCP 客户端不处理损坏的服务器在 C 中正确连接

    mysql - Processlist中有这么多Binlog_dump连接?

    java - Python 中相当于 Java InputStream 的 available 方法是什么?

    python - 如何阻止正则表达式过滤掉 4 个数字中的 2 个数字?

    c - 哪种是实现 TCP/UDP 服务器的最佳方式?在线程或进程中处理每个传入请求

    c++ - 构造函数失败后初始化 boost::asio 套接字

    python - 来自 BioPython 的 codeml 永远不会完成

    python - 用 Python 抓取具有可点击内容的网站

    c++ - 如何检测我是否有可从套接字读取的内容? (c++)