File transmission over TCP with Python
I'm currently working on a python project, that requires file transmission from a client to a server via the Python socket. Here is my current code, that however doesn't transmit the whole file, but there are always some bites missing, or additional bytes, according to the file sizes.
_con and con are connection wrappers connections via the python socket.
Client:
def _sendFile(self, path):
sendfile = open(path, 'rb')
data = sendfile.read()
self._con.sendall(data)
self._con开发者_Python百科.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')
While the first problem you have is with not writing the last chunk of data received to the output file, you have a few other problems.
Your current problem can be fixed by changing the if
statement to something like the following:
if (rec.endswith(b'FIN')):
writefile.write(rec[:-3]) # Ignore the last 3 bytes
break
You still have other problems:
If the file contains the characters
FIN
, there is a chance of about 1 in 1024 that the characters will be the last 3 in the read buffer, and your code will mistakenly read that as the end marker, and terminate prematurely.There is also a chance of 2 in 1024 that the
FIN
marker will be split over two calls toread()
, withrec
ending with eitherF
orFI
.
Both of these problems are caused by TCP being a stream based protocol, with no concept of packets at the user level.
One obvious fix for this is to precede the transmission of the file with a fixed size length indication, with the receiver reading this, and then reading the correct number of bytes.
Something like this:
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
Of course, when sending / receiving the length, you need to be aware of the ordering of bytes within the length field.
In this receive function loop above, you check if the received data ends with FIN, if it does. You just break out of the loop without writing it to the file. So you will be missing the last piece.
while (1):
rec = self.con.recv(1024)
if (rec.endswith(b'FIN')):
break
writefile.write(rec)
精彩评论