I have a standard forking TCPServer setup that receives incoming requests and sends back a file to a client. The server appears to be sending all the data, but I've checked client side that the bytes received != the bytes sent.
After further investigation, the receive method client side indicated that the server was closing the connection early - causing the receive to fail.
So then I modified the server to sleep for a couple seconds after sending the file - keeping the socket open long enough for the client to receive and then closing it. This works but it's very hackish in my opinion because it's hard to predict how long the thread should sleep before closing the socket.
I have tried setting SO_LINGER server side to keep the connection alive instead, but it doesn't help - even though I think it should.
There has to be a better way to block until the client fully receives the file. What do I need to do to guarantee the socket does not close until the client receives all the data?
Server
class ForkingTCPRequestHandler(SocketServer.BaseRequestHandler): def createSPP(self, dataLen, success): SPPStruct = struct.Struct('I?') values = (socket.htonl(dataLen), success,) packed_data = SPPStruct.pack(*values) return packed_data def handle(self): """Enabling SO_LINGER to keep connection alive doesn't help""" self.request.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 5)) """Send a packet to the client so it knows the length of incoming data""" spp = self.createSPP(os.path.getsize(FILE_NAME), 1) self.request.sendall(spp) """Sending the file, finish() is automatically called after this.""" f = open(FILE_NAME, 'rb') fileData = f.read() self.request.sendall(fileData) f.close() def finish(self): """Sleep until the file is fully received by the client. Sleeping keeps the connection open. BaseRequestHandler automatically closes the connection when finish() returns. This works but is not a robust solution.""" time.sleep(5) class ForkingTCPServer(SocketServer.ForkingMixIn, SocketServer.TCPServer): pass if __name__ == '__main__': try: server = ForkingTCPServer((HOST, PORT), ForkingTCPRequestHandler) except socket.error as e: sys.exit(1) try: server.serve_forever() except KeyboardInterrupt: server.shutdown() sys.exit(0) Client Connecting to the Server
// Establishes a standard TCP connection memset(&targetAddr, 0, sizeof(targetAddr)); targetAddr.sin_family = AF_INET; targetAddr.sin_port = htons(atoi(port)); bcopy(hostdetails->h_addr, (char *)&targetAddr.sin_addr, hostdetails->h_length); sock = socket(AF_INET, SOCK_STREAM, 0); if (socket < 0) { return -1; } rc = connect(sock, (struct sockaddr *)&targetAddr, sizeof(targetAddr)); if (rc < 0) { close(sock); return -1; } Client Receiving
// Receiving spp (server side) known as symProcPacket (client side) // symProcPacket contains the length of the file that will be sent next // Receiving this packet is always successful typedef struct SymProcessPacket { u_int32_t totalDataLen; BOOL processingSuccessful; } SymProcessPacket; tempBuf = (char *)malloc(sizeof(SymProcessPacket)); recvBytes = recv(s, tempBuf, sizeof(SymProcessPacket), 0); if (recvBytes < 0) { goto processingError; } memcpy(&symProcPacket, tempBuf, sizeof(SymProcessPacket)); free(tempBuf); // Receiving the file // Receive chunks and put in a buffer until entire file is received tempBuf = (char*) malloc(sizeof(char)*ntohl(symProcPacket.totalDataLen)); totalRecv = 0; recvBytes = 0; while (totalRecv < ntohl(symProcPacket.totalDataLen)) { recvBytes = recv(sock, tempBuf+totalRecv, (1<<14), 0); if (recvBytes < 0) { // RecvBytes returns -1, which is an error before getting all the data // It gets a "Connection was reset by peer" error here, unless the server // sleeps for a bit. It means the server closed the connection early. printf("Error: %s", errtostr(errno)); goto errorImporting; } totalRecv += recvBytes; }