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;
}
I am skipping your code part. I will just stay focused on the your problem, receiving complete file. One cool way would be adapting HTTP way. At first, just send the amount of bytes you are going to send so that the receiver end will receive from socket just up to that amount. So, just sending little additional data to receiver will do the work..
sender:
- Send the size of file to be sent
- Send the file data
receiver:
- Receive file size
- Receive data until len(data)==size_received
I can't figure out why sleeping has solved anything.
But I think python in sending 5 bytes and C++ is reading 8 bytes.?
in python takes on byte. BOOL I believe is typedefed as an int and takes probably 4 bytes.
In general reading/writing structs to sockets is discouraged.
精彩评论