套接字Python 3.5:套接字服务器在文件接收时永远挂起

时间:2016-08-10 21:30:16

标签: python sockets

我正在尝试编写一个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())

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

我对网络编程一般都是新手,如果我错过了一些明显的东西,请原谅我。

谢谢!

2 个答案:

答案 0 :(得分: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()

答案 1 :(得分:1)

您的代码存在一些问题。

首先:

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

这会导致在发出data语句时将文件部分加载到get变量。这就是为什么你没有获得完整档案的原因。

现在这个:

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,则必须将其转义,即需要额外的内容处理(以及另一方的解释)。