(Python)获取最大递归错误,在这种情况下我应该尝试其他方法还是重写它?

(Python)获取最大递归错误,在这种情况下我应该尝试其他方法还是重写它?,python,sockets,recursion,Python,Sockets,Recursion,我使用递归从套接字编写文件,我可以理解为什么会出现最大递归错误。我的问题是,我应该忽略它吗 以下是所需的行为: 对于第一个数据块,覆盖或创建以“wb”模式打开的文件。对于每个剩余的数据块,追加。如果我不这样做,它将在“ab”模式下向现有文件追加数据,或者它将只在“wb”模式下将每个块写入其自己的文件 以下是我的解决方案: def write_file(path:str, client_socket:socket, recv_len: int = 1, open_mode: str = &qu

我使用递归从套接字编写文件,我可以理解为什么会出现最大递归错误。我的问题是,我应该忽略它吗

以下是所需的行为:

  • 对于第一个数据块,覆盖或创建以“wb”模式打开的文件。对于每个剩余的数据块,追加。如果我不这样做,它将在“ab”模式下向现有文件追加数据,或者它将只在“wb”模式下将每个块写入其自己的文件
以下是我的解决方案:

def write_file(path:str, client_socket:socket, recv_len: int = 1, open_mode: str = "wb"):
    if recv_len:
        data = client_socket.recv(BUFFER_LEN)
        recv_len = len(data)

        with open(path, open_mode) as f:
            f.write(data)

        if recv_len < BUFFER_LEN:
            client_socket.send("File successfully transfered.")
            return
        else:
            write_file(path, client_socket, recv_len, "ab")
有些事告诉我,我会听到很多“嗯,这个限制是有充分理由的”,我不怀疑这一点。但是,既然我的循环解决方案感觉杂乱和古怪,而我的递归解决方案进入了这个巨大的递归冒险——那么有什么更好的方法可以继续前进呢?还有其他我没有看到的选择吗

编辑:我省略了尝试,除了递归模型,只是为了看看它是否有效。我打算把它们放回去


编辑:在传输之后,我也没有关闭套接字,使
not data
状态成为我无法使用的状态。

这方面的递归解决方案是有问题的,因为它会多次打开同一个文件,从而导致错误

虽然
方法是正确的方法,但应该正确实施

您应该打开文件一次,然后调用
socket.recv
,直到文件为空。当文件不为空时,写入该文件。最后关闭文件(或使用打开的

在Python>=3.8中,可以使用赋值表达式(
:=
)运算符进一步简化此操作:

def write_file(path:str, client_socket:socket, open_mode: str = "wb"):
    with open(path, open_mode) as f:
        while data := client_socket.recv(BUFFER_LEN):
            f.write(data)
    client_socket.send(b"Successfully saved file.")

您可以让python使用缓存下载完成所有工作

代码:

重要提示。只有在发送所有内容后服务器关闭连接,此方法才能正常工作

如果您知道文件的大小,则可以执行以下操作:

def write_file(path:str, client_socket:socket, file_size:int, open_mode: str="wb"):
    with open(path, open_mode, BUFFER_LEN) as f, client_socket.makefile("rb", BUFFER_LEN) as sf:
        f.write(sf.read(file_size))
    client_socket.send(b"Successfully saved file.")

它将准确读取您在
文件大小中传递的字节数,并且不需要关闭套接字。您可以在消息的前几个字节中传递文件大小但是它会将所有数据加载到内存中,因此此方法只能应用于较小的文件。我可以实现缓冲,但在这种情况下,与其他答案没有区别,所以我将它保持原样。

这恰好可以很快工作。这里的区别是我停止了多次打开该文件,根据上面的建议,它只打开了两次

提供的答案依赖于关闭发送套接字,而此答案不需要断开连接才能成功写入任意文件长度的文件

def download(path, client_socket):

    while True:
            file_buffer = b""
            recv_len = 1
                    
            try:
                while recv_len:
                    data = client_socket.recv(BUFFER_LEN)
                    recv_len = len(data)

                    # Overwrite file if exists.
                    with open(path, 'wb') as f:
                        f.write(data)

                    if recv_len < BUFFER_LEN:
                        break

                    else:
                        with open(path, 'ab') as f:
                            while True:
                                data = client_socket.recv(BUFFER_LEN)
                                recv_len = len(data)

                                # Append rest of data chunks
                                f.write(data)

                                if recv_len < BUFFER_LEN:
                                    break
                    break

                if not data:
                    break

                client_socket.send(b"Successfully saved file.")

            except:
                client_socket.send(b"File transfer failed.")
def下载(路径、客户端\u套接字):
尽管如此:
文件缓冲区=b“”
记录长度=1
尝试:
而recv_len:
数据=客户端\u套接字.recv(缓冲区\u LEN)
记录长度=长度(数据)
#覆盖文件(如果存在)。
将open(路径“wb”)作为f:
f、 写入(数据)
如果记录长度<缓冲长度:
打破
其他:
打开(路径“ab”)作为f:
尽管如此:
数据=客户端\u套接字.recv(缓冲区\u LEN)
记录长度=长度(数据)
#附加其余的数据块
f、 写入(数据)
如果记录长度<缓冲长度:
打破
打破
如果没有数据:
打破
客户端_socket.send(b“已成功保存文件”)
除:
客户端_socket.send(b“文件传输失败”)

在您的第一个示例(使用递归)中,使用默认的
模式
每次调用函数时都覆盖上一个块,通常每次迭代都重新打开文件是非常无效的。我将“ab”作为递归调用的模式。我看不出您的两个解决方案是如何等效的。您的迭代方法能够捕获一些异常。我确信递归方法是错误的,因为出口依赖于外部数据。然而,我也确信你的迭代版本会更整洁。没错,我确实省略了try/除了递归版本,只是因为我想先看看它是否有效。这是一个很棒的赋值表达式,谢谢分享。但是,套接字在这种情况下并不友好,除非我在文件发送后关闭发送连接。这要优雅得多,但我从未进入
not data
状态,因为我不会在文件发送后关闭发送套接字。@J.Krajewski,您也可以申请让套接字模块使用缓冲区完成所有这些工作。这很有趣,但是在实现了这一点之后,我仍然存在挂起套接字的问题。因为我没有关闭发送套接字,所以我从未关闭过。此外,如果我将缓冲设置为false,它将永远持续。最后,我不确定这是否有助于解决我的特殊问题,但这是一个有趣的解决方案。谢谢第二,当我使用BUFFER_LEN时,它在4096字节后停止接收。@J.Krajewski,但我用比我接收的数据小的BUFFER_LEN进行了测试,它工作得非常好。另外,关于悬挂式插座-如果需要,您应该在函数调用后关闭插座。@J.Krajewski,检查。在我的原始帖子中,我有一行
if recv_len
在我的情况下,这是恢复悬挂式插座所必需的。如果我关闭套接字,它似乎完成了文件,但如果我不关闭,它将挂起而不写入最后一个缓冲区。
def write_file(path:str, client_socket:socket, open_mode: str = "wb"):
    with open(path, open_mode) as f:
        while data := client_socket.recv(BUFFER_LEN):
            f.write(data)
    client_socket.send(b"Successfully saved file.")
import socket
import shutil

def write_file(path:str, client_socket:socket, open_mode:str="wb"):
    with open(path, open_mode, BUFFER_LEN) as f, client_socket.makefile("rb", BUFFER_LEN) as sf:
        shutil.copyfileobj(sf, f)
    client_socket.send(b"Successfully saved file.")
def write_file(path:str, client_socket:socket, file_size:int, open_mode: str="wb"):
    with open(path, open_mode, BUFFER_LEN) as f, client_socket.makefile("rb", BUFFER_LEN) as sf:
        f.write(sf.read(file_size))
    client_socket.send(b"Successfully saved file.")
def download(path, client_socket):

    while True:
            file_buffer = b""
            recv_len = 1
                    
            try:
                while recv_len:
                    data = client_socket.recv(BUFFER_LEN)
                    recv_len = len(data)

                    # Overwrite file if exists.
                    with open(path, 'wb') as f:
                        f.write(data)

                    if recv_len < BUFFER_LEN:
                        break

                    else:
                        with open(path, 'ab') as f:
                            while True:
                                data = client_socket.recv(BUFFER_LEN)
                                recv_len = len(data)

                                # Append rest of data chunks
                                f.write(data)

                                if recv_len < BUFFER_LEN:
                                    break
                    break

                if not data:
                    break

                client_socket.send(b"Successfully saved file.")

            except:
                client_socket.send(b"File transfer failed.")