从并发 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 的内容。