从并发 TCP 服务器下载多个文件:奇怪的结果和 "Pipe Error"
Downloading multiple files from a concurrent TCP server: strange results and "Pipe Error"
我几乎完成了一个并发(基于进程)python TCP 服务器的构建,它接受来自客户端的连续命令。客户端可以从服务器下载文件。
截至目前,客户端可以通过键入 'DL bob.jpg' 成功下载光荣的 Bob Ross ('bob.jpg') 的图像。这将使用 str[3:] 作为下载命令的输入发送字符串 bob.jpg。
客户端可以下载图像 'bob.jpg' 看似无限次。如果客户端下载文件'Battlefield.mp3',文件下载成功,但是下载命令将不再起作用,除非中间输入了另一个命令(例如DIR)。换句话说:如果客户端尝试下载 'Battlefield.mp3' 两次或一次然后 'bob.jpg',则会引发错误。
它奇怪地特定于 Battlefield.mp3 - 我将尝试上传另一个 mp3 并测试它,看看我是否可以在我的结果中复制和编辑。
我的服务器下载代码的工作方式是:它首先调用 socket.send() 发送一条消息,其中包含要下载的文件的文件名和文件大小。发送的字符串类似于 "bob.jpg|93174",我的客户端解析它(使用 split("|"))并将它们用作变量。服务器将准确读取文件正在下载的字节数,客户端将写入完全相同的字节数(并以相同的文件名调用文件)。
客户
#~~~~~DOWNLOAD~~~~~#
elif nextCommand.find("DL") == 0 or nextCommand.find("dl") == 0:
sendPath = nextCommand[3:]
print "Attempting download using filepath as: " , sendPath
fileInfo = s.recv(1024)
print "Download Request Acknowledged from server" # , fileInfo
if fileInfo.find("Error") == 0:
print "Error Opening File"
else:
if fileInfo:
fL = fileInfo.split("|")
dlPath = fL[0]
fileSize = fL[1]
f = open(dlPath, 'wb+') #Download Target Path
print "Saving file as:" , f.name
print "Recieved File Size as:" , fileSize
fSize = int(fileSize)
fileContent = s.recv(fSize)
f.write(fileContent)
print "Download Complete"
f.close()
服务器
elif data.find("DL") == 0 or data.find("dl") == 0:
printTime(data)
fPath = data[3:]
try:
file = open(fPath,'rb+')
print "Opened File: " , file.name
fSize = os.path.getsize(fPath)
print "File Size: " , fSize
strSize = str(fSize)
seq = (file.name, strSize)
msg = "|".join(seq)
clientsocket.send(msg)
fileContent = file.read(fSize)
clientsocket.send(fileContent)
file.close()
print "Sent and closed file"
except IOError:
msg = "Error Opening File"
print msg
clientsocket.send(msg)
continue
客户端输出:
Attempting download using filepath as: Battlefield.mp3
Download Request Acknowledged from server
Saving file as: Battlefield.mp3
Recieved File Size as: 5410198
Download Complete
Enter Command: dl download.txt
Sending Command: dl download.txt
Attempting download using filepath as: download.txt
Download Request Acknowledged from server
Traceback (most recent call last):
File "cli.py", line 74, in <module>
f = open(dlPath, 'wb+') #Download Target Path
TypeError: file() argument 1 must be encoded string without NULL bytes, not str
服务器出局:
Opened File: Battlefield.mp3
File Size: 5410198
Sent and closed file
Recived dl download.txt from: ('127.0.0.1', 34858) | 2015-12-06 21:05:15.453932
Opened File: download.txt
File Size: 33
Error Opening File
Process Process-16:
Traceback (most recent call last):
File "/usr/lib64/python2.6/multiprocessing/process.py", line 232, in _bootstrap
self.run()
File "/usr/lib64/python2.6/multiprocessing/process.py", line 88, in run
self._target(*self._args, **self._kwargs)
File "serv.py", line 115, in handler
clientsocket.send(msg)
error: [Errno 32] Broken pipe
编辑:
尝试使用不同的 mp3 会出现不同的错误:
Attempting download using filepath as: unity.mp3
Download Request Acknowledged from server
Saving file as: unity.mp3
Recieved File Size as: 3985398
Download Complete
Enter Command: dl unity.mp3
Sending Command: dl unity.mp3
Attempting download using filepath as: unity.mp3
Download Request Acknowledged from server
Saving file as: êa¾è¸Ôq·s¡
Recieved File Size as:
õ¯vd22û¥$#P®Gµ7ýÿõ-¶ÛXÝ^m±ÆçìÎÅéM¤Vp6OK¦ÿûDwXVá
+pU«
§n
]a[æ ÁL,0
Traceback (most recent call last):
File "cli.py", line 77, in <module>
fSize = int(fileSize)
ValueError: invalid literal for int() with base 10: '\x85\x97\xf5\xafvd\x8922\xfb\xa5$#P\xaeG\xa3\x08\xb57\xfd\x93\xff\xf5\x1c-\xb6\xdbX\xdd^\x9d\xec\xef\x08\xea\x95#\xb0\x11\x14\xe3]\x0f\x84I\xa4z\x19f\x89\x19U\x8e\xc5 \xa9\xcfW#U\xdf\xe7Bs\x1d\x18\xdb\xad\xdc\xa092s\xcc\x1d\xd7Y\xccy\x8a\xbb1\x89\xbb]\xd8\xf6a\xce\xfe\xf5\xa2w\x8e\xd9\x98pt\xad\x99\x17\xe6\xab\xb7\xff\x95\x18\xfe\xa9\xb9\xaa\xa8\xaaJSi\xb4"K6\x96\x85\xa9l\x10\xfe\xa3O\xac\xd0'
发送的似乎是实际文件的代码,而不是有关文件本身的信息。
这里有很多代码,但正如我怀疑的那样,有一件事导致了所有这些错误(或者至少是您在编辑中发布的错误 - 服务器错误看起来更像是 IPC 中的错误)。
所以,这样做
fileInfo = s.recv(1024)
...
fSize = int(fileSize)
fileContent = s.recv(fSize)
f.write(fileContent)
不幸的是,您已经下载了文件 (print "Download Complete"
) 是错误的。 TCP 传输不保留其边界。在一个 send
中发送 1000 个字节并不意味着你只需要一个 recv
就可以得到它。一条 TCP 消息在传输过程中可能会分片。多个 TCP 消息可以作为一个到达。唯一可以保证的是按顺序交货。
最后一个错误似乎说明了这种情况,您将文件大小与文件的一部分连接在一起。
为了克服这种情况,我建议您阅读一些有关 TCP 和 this article about TCP message framing 的内容。
我几乎完成了一个并发(基于进程)python TCP 服务器的构建,它接受来自客户端的连续命令。客户端可以从服务器下载文件。
截至目前,客户端可以通过键入 'DL bob.jpg' 成功下载光荣的 Bob Ross ('bob.jpg') 的图像。这将使用 str[3:] 作为下载命令的输入发送字符串 bob.jpg。
客户端可以下载图像 'bob.jpg' 看似无限次。如果客户端下载文件'Battlefield.mp3',文件下载成功,但是下载命令将不再起作用,除非中间输入了另一个命令(例如DIR)。换句话说:如果客户端尝试下载 'Battlefield.mp3' 两次或一次然后 'bob.jpg',则会引发错误。
它奇怪地特定于 Battlefield.mp3 - 我将尝试上传另一个 mp3 并测试它,看看我是否可以在我的结果中复制和编辑。
我的服务器下载代码的工作方式是:它首先调用 socket.send() 发送一条消息,其中包含要下载的文件的文件名和文件大小。发送的字符串类似于 "bob.jpg|93174",我的客户端解析它(使用 split("|"))并将它们用作变量。服务器将准确读取文件正在下载的字节数,客户端将写入完全相同的字节数(并以相同的文件名调用文件)。
客户
#~~~~~DOWNLOAD~~~~~#
elif nextCommand.find("DL") == 0 or nextCommand.find("dl") == 0:
sendPath = nextCommand[3:]
print "Attempting download using filepath as: " , sendPath
fileInfo = s.recv(1024)
print "Download Request Acknowledged from server" # , fileInfo
if fileInfo.find("Error") == 0:
print "Error Opening File"
else:
if fileInfo:
fL = fileInfo.split("|")
dlPath = fL[0]
fileSize = fL[1]
f = open(dlPath, 'wb+') #Download Target Path
print "Saving file as:" , f.name
print "Recieved File Size as:" , fileSize
fSize = int(fileSize)
fileContent = s.recv(fSize)
f.write(fileContent)
print "Download Complete"
f.close()
服务器
elif data.find("DL") == 0 or data.find("dl") == 0:
printTime(data)
fPath = data[3:]
try:
file = open(fPath,'rb+')
print "Opened File: " , file.name
fSize = os.path.getsize(fPath)
print "File Size: " , fSize
strSize = str(fSize)
seq = (file.name, strSize)
msg = "|".join(seq)
clientsocket.send(msg)
fileContent = file.read(fSize)
clientsocket.send(fileContent)
file.close()
print "Sent and closed file"
except IOError:
msg = "Error Opening File"
print msg
clientsocket.send(msg)
continue
客户端输出:
Attempting download using filepath as: Battlefield.mp3
Download Request Acknowledged from server
Saving file as: Battlefield.mp3
Recieved File Size as: 5410198
Download Complete
Enter Command: dl download.txt
Sending Command: dl download.txt
Attempting download using filepath as: download.txt
Download Request Acknowledged from server
Traceback (most recent call last):
File "cli.py", line 74, in <module>
f = open(dlPath, 'wb+') #Download Target Path
TypeError: file() argument 1 must be encoded string without NULL bytes, not str
服务器出局:
Opened File: Battlefield.mp3
File Size: 5410198
Sent and closed file
Recived dl download.txt from: ('127.0.0.1', 34858) | 2015-12-06 21:05:15.453932
Opened File: download.txt
File Size: 33
Error Opening File
Process Process-16:
Traceback (most recent call last):
File "/usr/lib64/python2.6/multiprocessing/process.py", line 232, in _bootstrap
self.run()
File "/usr/lib64/python2.6/multiprocessing/process.py", line 88, in run
self._target(*self._args, **self._kwargs)
File "serv.py", line 115, in handler
clientsocket.send(msg)
error: [Errno 32] Broken pipe
编辑:
尝试使用不同的 mp3 会出现不同的错误:
Attempting download using filepath as: unity.mp3
Download Request Acknowledged from server
Saving file as: unity.mp3
Recieved File Size as: 3985398
Download Complete
Enter Command: dl unity.mp3
Sending Command: dl unity.mp3
Attempting download using filepath as: unity.mp3
Download Request Acknowledged from server
Saving file as: êa¾è¸Ôq·s¡
Recieved File Size as:
õ¯vd22û¥$#P®Gµ7ýÿõ-¶ÛXÝ^m±ÆçìÎÅéM¤Vp6OK¦ÿûDwXVá
+pU«
§n
]a[æ ÁL,0
Traceback (most recent call last):
File "cli.py", line 77, in <module>
fSize = int(fileSize)
ValueError: invalid literal for int() with base 10: '\x85\x97\xf5\xafvd\x8922\xfb\xa5$#P\xaeG\xa3\x08\xb57\xfd\x93\xff\xf5\x1c-\xb6\xdbX\xdd^\x9d\xec\xef\x08\xea\x95#\xb0\x11\x14\xe3]\x0f\x84I\xa4z\x19f\x89\x19U\x8e\xc5 \xa9\xcfW#U\xdf\xe7Bs\x1d\x18\xdb\xad\xdc\xa092s\xcc\x1d\xd7Y\xccy\x8a\xbb1\x89\xbb]\xd8\xf6a\xce\xfe\xf5\xa2w\x8e\xd9\x98pt\xad\x99\x17\xe6\xab\xb7\xff\x95\x18\xfe\xa9\xb9\xaa\xa8\xaaJSi\xb4"K6\x96\x85\xa9l\x10\xfe\xa3O\xac\xd0'
发送的似乎是实际文件的代码,而不是有关文件本身的信息。
这里有很多代码,但正如我怀疑的那样,有一件事导致了所有这些错误(或者至少是您在编辑中发布的错误 - 服务器错误看起来更像是 IPC 中的错误)。
所以,这样做
fileInfo = s.recv(1024)
...
fSize = int(fileSize)
fileContent = s.recv(fSize)
f.write(fileContent)
不幸的是,您已经下载了文件 (print "Download Complete"
) 是错误的。 TCP 传输不保留其边界。在一个 send
中发送 1000 个字节并不意味着你只需要一个 recv
就可以得到它。一条 TCP 消息在传输过程中可能会分片。多个 TCP 消息可以作为一个到达。唯一可以保证的是按顺序交货。
最后一个错误似乎说明了这种情况,您将文件大小与文件的一部分连接在一起。
为了克服这种情况,我建议您阅读一些有关 TCP 和 this article about TCP message framing 的内容。