Python 套接字,从服务器请求文件然后等待接收
Python Sockets, requesting file from server then waiting to receive it
我正在尝试从我的客户端向我的服务器发送一个具有特定文件名的字符串,然后将该文件发送给客户端。由于某种原因,即使在收到所有文件后它也会挂起。它挂在:
m = s.recv(1024)
client.py
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("192.168.1.2", 54321))
s.send(b"File:test.txt")
f = open("newfile.txt", "wb")
data = None
while True:
m = s.recv(1024)
data = m
if m:
while m:
m = s.recv(1024)
data += m
else:
break
f.write(data)
f.close()
print("Done receiving")
server.py
import socket
import os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", 54321))
while True:
client_input = c.recv(1024)
command = client_input.split(":")[0]
if command == "File":
command_parameter = client_input.split(":")[1]
f = open(command_parameter, "rb")
l = os.path.getsize(command_parameter)
m = f.read(l)
c.sendall(m)
f.close()
TLDR
之所以recv
阻塞是因为发送文件数据后socket连接没有关闭。目前的实现无法知道通信何时结束,这会导致两个远程进程之间出现死锁。为避免这种情况,请关闭服务器中的套接字连接,这将在客户端中生成一个 end-of-file 事件(即 recv
returns 一个 zero-length 字符串)。
更多见解
每当你设计任何两个进程相互通信的软件时,你都必须定义一个 协议 来消除通信的歧义,这样两个对等方就可以确切地知道它们处于哪种状态一直。通常,这涉及使用通信语法来帮助指导数据的解释。
目前,您的实施存在一些问题:它没有定义足够的协议来解决潜在的歧义。当您考虑到一个对等方中对 send
的每次调用不一定与另一个对等方对 recv
的一次调用完全对应这一事实时,这一点就变得很明显了。也就是说,对 send
和 recv
的调用不一定是 one-to-one。考虑将文件名发送到严重拥塞网络上的服务器:当第一次调用 recv
returns 时,可能只有一半的文件名发送到服务器。服务器无法(当前)知道它是否已完成接收文件名。在客户端也是如此:客户端如何知道文件何时完成?
为了解决这个问题,我们可以在协议中引入一些语法,在服务器中引入一些逻辑,以确保我们在继续之前获得完整的文件名。一个简单的解决方案是使用 EOL 字符,即 \n
来表示客户端消息的结尾。现在,在您的测试中,99.99% 的时间都需要一次调用 recv
来读入。但是您必须预料到可能需要多次调用 recv
的情况。显然,这可以使用循环来实现。
这个demo客户端比较简单。如果在发送文件后通信结束,则该事件可用于表示数据流结束。当服务器关闭连接时会发生这种情况。
如果我们要扩展实现,比如说,允许请求多个 back-to-back 文件,那么我们必须在协议中引入一些机制来区分一个文件的结尾和下一个的开始。请注意,这也意味着服务器可能需要缓冲它在先前迭代中读入的额外字节,以防出现重叠。流实现通常对这类事情很有用。
我正在尝试从我的客户端向我的服务器发送一个具有特定文件名的字符串,然后将该文件发送给客户端。由于某种原因,即使在收到所有文件后它也会挂起。它挂在:
m = s.recv(1024)
client.py
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("192.168.1.2", 54321))
s.send(b"File:test.txt")
f = open("newfile.txt", "wb")
data = None
while True:
m = s.recv(1024)
data = m
if m:
while m:
m = s.recv(1024)
data += m
else:
break
f.write(data)
f.close()
print("Done receiving")
server.py
import socket
import os
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", 54321))
while True:
client_input = c.recv(1024)
command = client_input.split(":")[0]
if command == "File":
command_parameter = client_input.split(":")[1]
f = open(command_parameter, "rb")
l = os.path.getsize(command_parameter)
m = f.read(l)
c.sendall(m)
f.close()
TLDR
之所以recv
阻塞是因为发送文件数据后socket连接没有关闭。目前的实现无法知道通信何时结束,这会导致两个远程进程之间出现死锁。为避免这种情况,请关闭服务器中的套接字连接,这将在客户端中生成一个 end-of-file 事件(即 recv
returns 一个 zero-length 字符串)。
更多见解
每当你设计任何两个进程相互通信的软件时,你都必须定义一个 协议 来消除通信的歧义,这样两个对等方就可以确切地知道它们处于哪种状态一直。通常,这涉及使用通信语法来帮助指导数据的解释。
目前,您的实施存在一些问题:它没有定义足够的协议来解决潜在的歧义。当您考虑到一个对等方中对 send
的每次调用不一定与另一个对等方对 recv
的一次调用完全对应这一事实时,这一点就变得很明显了。也就是说,对 send
和 recv
的调用不一定是 one-to-one。考虑将文件名发送到严重拥塞网络上的服务器:当第一次调用 recv
returns 时,可能只有一半的文件名发送到服务器。服务器无法(当前)知道它是否已完成接收文件名。在客户端也是如此:客户端如何知道文件何时完成?
为了解决这个问题,我们可以在协议中引入一些语法,在服务器中引入一些逻辑,以确保我们在继续之前获得完整的文件名。一个简单的解决方案是使用 EOL 字符,即 \n
来表示客户端消息的结尾。现在,在您的测试中,99.99% 的时间都需要一次调用 recv
来读入。但是您必须预料到可能需要多次调用 recv
的情况。显然,这可以使用循环来实现。
这个demo客户端比较简单。如果在发送文件后通信结束,则该事件可用于表示数据流结束。当服务器关闭连接时会发生这种情况。
如果我们要扩展实现,比如说,允许请求多个 back-to-back 文件,那么我们必须在协议中引入一些机制来区分一个文件的结尾和下一个的开始。请注意,这也意味着服务器可能需要缓冲它在先前迭代中读入的额外字节,以防出现重叠。流实现通常对这类事情很有用。