【问题标题】:Sockets Python 3.5: Socket server hangs forever on file receive套接字 Python 3.5:套接字服务器在文件接收时永远挂起
【发布时间】:2017-03-27 15:37:11
【问题描述】:

我正在尝试编写一个 Python 程序,如果客户端连接到服务器,它可以浏览目录并使用套接字抓取文件。浏览部分工作正常,它打印出客户端的所有目录。 以下是部分代码:

with clientsocket:
    print('Connected to: ', addr)
    while True:
        m = input("Command > ")

        clientsocket.send(m.encode('utf-8'))
        data = clientsocket.recv(10000)

        if m == "exit":
            clientsocket.close()
        if m.split()[0] == 'get':
            inp = input("Filename > ")
            while True:
                rbuf = clientsocket.recv(8192)                
                if not rbuf:
                    break

                d = open(inp, "ab")
                d.write(rbuf)
                d.close()


        elif data.decode('utf-8').split()[0] == "LIST":
            print(data.decode('utf-8'))
        if not data:
            break

但是,问题出在这里:

if m.split()[0] == 'get':
    inp = input("Filename > ")
    while True:
        rbuf = clientsocket.recv(8192)                
        if not rbuf:
            break

它似乎陷入了无限循环。更有意思的是我试图接收的文件是88.3kb,但是文件在循环中返回的是87kb,非常接近……

我也尝试过一次接收一个 python 脚本(没有循环),它工作正常。

这是一些客户端代码:

while True:
    msg = s.recv(1024).decode('utf-8')


    if msg.split()[0] == "list":
        dirs = os.listdir(msg.split()[1])
        string = ''
        for dira in dirs:
            string += "LIST " + dira + "\n"
        s.send(string.encode('utf-8'))
    elif msg == "exit":
        break
    else:
        #bit that sends the file
        with open(msg.split()[1], 'rb') as r:
            s.sendall(r.read())

所以我的问题是,如果我将它设置为在没有数据时关闭,为什么它会陷入无限循环,我该如何解决这个问题?

总的来说,我对网络编程有点陌生,所以如果我遗漏了一些明显的东西,请原谅我。

谢谢!

【问题讨论】:

  • 尝试...除了... ???
  • 之前那个是调试用的,估计不用了。

标签: python sockets


【解决方案1】:

我想我知道问题出在哪里,但我可能错了。我遇到过几次,即使我指定了正确的长度,一个recv 呼叫也没有收到整个消息。但是,您没有到达流的末尾,因此您的程序一直在等待剩余的 8192 个字节,而这些字节永远不会到达。

试试这个:
发送文件:

#bit that sends the file
with open(msg.split()[1], 'rb') as r:
   data = r.read()
   # check data length in bytes and send it to client
   data_length = len(data)
   s.send(data_length.to_bytes(4, 'big'))
   s.send(data)
s.shutdown(socket.SHUT_RDWR)
s.close()

接收文件:

# check expected message length
remaining = int.from_bytes(clientsocket.recv(4), 'big')
d = open(inp, "wb")
while remaining:
    # until there are bytes left...
    # fetch remaining bytes or 4094 (whatever smaller)
    rbuf = clientsocket.recv(min(remaining, 4096))
    remaining -= len(rbuf)
    # write to file
    d.write(rbuf)
d.close()

【讨论】:

  • remaining = int.from_bytes(clientsocket.recv(4), 'big') 是如何工作的?
  • clientsocket.recv(4) 接收编码数据长度的前四个字节,然后使用 big-endian 字节顺序将字节转换为 int,与我们将 int 编码为字节时使用的相同。
【解决方案2】:

您的代码有几个问题。

第一:

clientsocket.send(m.encode('utf-8'))
data = clientsocket.recv(10000)

当您发出get 语句时,这会导致文件部分加载到data 变量。这就是为什么你没有得到完整的文件。

现在这个:

while True:
    rbuf = clientsocket.recv(8192)                
    if not rbuf:
        break
    ...

您确实加载了完整文件,但客户端从不关闭连接(发送文件后它转到s.recv())所以if 语句永远不会满足。因此,下载文件后,此循环在 clientsocket.recv(8192) 部分被阻止。

所以问题是您必须以某种方式通知下载器您已发送所有数据,即使连接仍处于打开状态。有几种方法可以做到这一点:

  1. 您计算文件的大小并将其作为前几个字节发送。例如,假设文件的内容是ala ma kota。这些是 11 个字节,因此您发送 \x11ala ma kota。现在接收器知道第一个字节是大小,它会这样解释它。当然,一个字节的标头并不多(您最多只能发送 256 个字节的文件),因此通常您会选择 4 个字节。所以现在你的客户端和服务器之间的协议是:前 4 个字节是文件的大小。因此,我们的初始文件将作为\x0\x0\x0\x11ala ma kota 发送。此解决方案的缺点是您必须在发送之前知道内容的大小。

  2. 您标记了流的结尾。因此,您选择一个特定的字符,例如X,然后阅读流,直到找到X。如果你这样做了,那么你就知道对方发送了它拥有的所有东西。缺点是如果X 在内容中,那么您必须对其进行转义,即需要额外的内容处理(以及另一边的解释)。

【讨论】:

    猜你喜欢
    • 2014-12-05
    • 2014-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-10
    • 1970-01-01
    相关资源
    最近更新 更多