开发者

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:

  1. 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.

  2. There is also a chance of 2 in 1024 that the FIN marker will be split over two calls to read(), with rec ending with either F or FI.

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)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜