使用Python通过TCP传输文件
我现在在做一个Python项目,需要通过Python的socket从客户端传输文件到服务器。这里是我现在的代码,但它并不能完整地传输整个文件,总是会缺少一些字节,或者根据文件的大小多出一些字节。
_con和con是通过Python socket建立的连接包装器。
客户端:
def _sendFile(self, path):
sendfile = open(path, 'rb')
data = sendfile.read()
self._con.sendall(data)
self._con.send(bytes('FIN', 'utf8'))
# Get Acknowledgement
self._con.recv(6)
def _recieveFile(self, path):
# Recieve the file from the client
writefile = open(path, 'wb')
i = 0
while (1):
rec = self.con.recv(1024)
if (rec.endswith(b'FIN')):
break
writefile.write(rec)
self.con.send(b'ACK')
2 个回答
0
在上面的接收函数循环中,你会检查接收到的数据是否以FIN结尾。如果是的话,你就直接跳出循环,而不把它写入文件。这样一来,你就会漏掉最后一部分数据。
while (1):
rec = self.con.recv(1024)
if (rec.endswith(b'FIN')):
break
writefile.write(rec)
6
你现在遇到的第一个问题是没有把最后一部分收到的数据写入输出文件,不过你还有其他一些问题。
你可以通过把if
语句改成下面这样的方式来解决当前的问题:
if (rec.endswith(b'FIN')):
writefile.write(rec[:-3]) # Ignore the last 3 bytes
break
不过,你还有其他问题:
如果文件里包含字符
FIN
,那么有大约1/1024的概率这些字符会出现在读取缓冲区的最后3个字符中,这样你的代码就会错误地把它当作结束标记,从而提前结束。还有大约2/1024的概率,
FIN
标记会被分成两次调用read()
来读取,这样rec
可能会以F
或FI
结尾。
这两个问题都是因为TCP是基于流的协议,用户层面没有数据包的概念。
一个明显的解决办法是在传输文件之前先发送一个固定大小的长度指示,接收方读取这个长度,然后再读取正确数量的字节。
像这样:
def _sendFile(self, path):
sendfile = open(path, 'rb')
data = sendfile.read()
self._con.sendall(encode_length(len(data)) # Send the length as a fixed size message
self._con.sendall(data)
# Get Acknowledgement
self._con.recv(1) # Just 1 byte
def _recieveFile(self, path):
LENGTH_SIZE = 4 # length is a 4 byte int.
# Recieve the file from the client
writefile = open(path, 'wb')
length = decode_length(self.con.read(LENGTH_SIZE) # Read a fixed length integer, 2 or 4 bytes
while (length):
rec = self.con.recv(min(1024, length))
writefile.write(rec)
length -= sizeof(rec)
self.con.send(b'A') # single character A to prevent issues with buffering
当然,在发送/接收长度的时候,你需要注意长度字段内字节的顺序。