开发者

How to keep Python server connection open until client is done with it?

开发者 https://www.devze.com 2023-02-21 15:52 出处:网络
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

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.

0

精彩评论

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