以下代码使我可以从服务器向客户端下载名为 tmp.bsp , tmp.seq 和 tmp.dms 的三个文件。但是,仅第一个文件 tmp.dms 已完全下载。另一个 tmp.seq 填充了 tmp.bsp 和 tmp.bsp 保持0KB的信息。
客户端:
numpy
import socket
import socket
skClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
skClient.connect(("127.0.0.1",2525))
sData = "Temp"
sData2 = "Temp"
sData3 = "Temp"
while True:
sData = skClient.recv(1024)
fDownloadFile = open("tmp.dms","wb")
sData2 = skClient.recv(1024)
fDownloadFile2 = open("tmp.seq","wb")
sData3 = skClient.recv(1024)
fDownloadFile3 = open("tmp.bsp","wb")
while sData:
fDownloadFile.write(sData)
sData = skClient.recv(1024)
fDownloadFile.close()
fDownloadFile2.write(sData2)
sData2 = skClient.recv(1024)
fDownloadFile2.close()
fDownloadFile3.write(sData3)
sData3 = skClient.recv(1024)
fDownloadFile3.close()
print "Download over"
break
skClient.close()
是一个计数器,其打印用于调试。
n
是要下载一个文件,并且可以正常工作,但是由于我要三个文件,所以我只对其发表了评论。
服务器:
sFileName
我正在使用的文件:
server2.py is my server
Execution
答案 0 :(得分:2)
代码的主要问题是您要发送/接收任意数量的数据。如果您的缓冲区(1024)小于文件大小,则客户端的文件将包含较少的信息,如果它较大,则文件可能包含更多的信息(来自下一个文件的数据)。
您可以通过发送一个表示文件结尾的值来解决此问题。此方法的问题在于,该值不能包含在任何文件中,并且客户端必须在接收到的数据中扫描该值。
另一种可能的解决方案是计算文件大小,然后在文件数据之前发送该信息。这样,管理员将知道每个文件需要多少数据。
使用struct.pack
,我们可以创建一个文件大小最小的四个字节的标头。
def send_file(soc, path):
with open(path, 'rb') as f:
data = f.read()
size = struct.pack('!I', len(data))
soc.send(size + data)
然后,客户端可以通过读取四个字节并将unpack
设置为int来获取文件大小。
def recv_file(soc, path):
size_header = soc.recv(4)
size = struct.unpack('!I', size_header)[0]
data = soc.recv(size)
with open(path, 'wb') as f:
f.write(data)
请注意,如果文件大小大于套接字缓冲区,则一次调用发送/接收文件可能会引起套接字错误。在这种情况下,您必须循环读取较小的数据块,或者使用socket.setsockopt
增加缓冲区大小。
这是上述功能的修改版本,可以处理大文件:
import struct
import os.path
def send_file(soc, path):
file_size = os.path.getsize(path)
size_header = struct.pack('!Q', file_size)
soc.send(size_header)
with open(path, 'rb') as f:
while True:
data = f.read(1024)
if not data:
break
soc.send(data)
def recv_file(soc, path):
size_header = soc.recv(8)
file_size = struct.unpack('!Q', size_header)[0]
chunks = [1024 for i in range(file_size / 1024)]
with open(path, 'wb') as f:
for chunk in chunks:
f.write(soc.recv(chunk))
f.write(soc.recv(file_size % 1024))
我还没有彻底测试过此代码,但是它适用于任何大小的文件。
在服务器中使用send_file
函数的示例:
host = ''
skServer = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
skServer.bind((host,2525))
skServer.listen(10)
print "Server currently active"
Content,Address = skServer.accept()
print Address
files = ['tmp.bsp', 'tmp.seq', 'tmp.dms']
for file in files:
send_file(Content, file)
Content.close()
print "Sending is over"
skServer.close()
在客户端中使用recv_file
:
skClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
skClient.connect(("127.0.0.1",2525))
files = ['tmp.bsp', 'tmp.seq', 'tmp.dms']
for file in files:
recv_file(skClient, file)
print "Download over"
skClient.close()
答案 1 :(得分:1)
是的,是的,我确实运行了您的程序并发现了完全相同的问题。我没有足够的时间在这个问题上做更多的工作,但是我发现一些关键点可能会导致您找到正确的解决方法。
https://docs.python.org/2/howto/sockets.html
上面的官方文件说:
recv返回0字节时,表示另一端已关闭(或正在关闭)连接。您将不会再通过此连接收到任何数据。曾经您也许可以成功发送数据
这是第三个文件返回0字节时发生的情况。
但是为什么合并第二个和第三个文件,我想是因为套接字只是缓冲文件,我们可能需要在发送另一个文件之前尝试确保缓冲是清除的。
阅读此内容
现在有两组动词可用于交流。您可以使用send和recv,也可以将客户端套接字转换为类似文件的野兽并使用读写。后者是Java呈现其套接字的方式。除了警告您需要在套接字上使用冲洗功能之外,我这里不再讨论。这些是缓冲的“文件”,一个常见的错误是写一些东西,然后阅读以得到答复。如果没有刷新,您可能会永远等待答复,因为请求可能仍在输出缓冲区中。
但是,如果您打算重复使用套接字以进行进一步的传输,则需要意识到套接字上没有没有 EOT。我重复一遍:如果套接字发送或接收后处理0字节后返回,则说明连接已断开。如果连接没有断开,您可以永远等待一次接收,因为套接字不会告诉您(现在)没有更多内容可供阅读。现在,如果您考虑一下,您将认识到套接字的基本原理:消息必须是固定长度(讨厌)或定界(耸耸肩),或者指出它们有多长(更好),或者通过关闭连接来结束。选择完全是您的选择(但某些方法比其他方法正确)。
希望这会有所帮助。
答案 2 :(得分:0)
我并不完全精通Python,但我认为您的while语句应类似于:
while: sData or sData2 or sData3
我的语法可能不正确,但是目前看来,当“ sData”完成时,您将停止,并且即使它们尚未完成,也将在那时停止下载sData2和aData3。
嗯-那或“ While”根本没有循环,而只是用作“ if”?在不知道API的情况下很难说出来。