Как сохранить соединение с сервером Python открытым, пока клиент не покончит с ним? - PullRequest
1 голос
/ 04 апреля 2011

У меня есть стандартная настройка разветвления TCPServer, которая получает входящие запросы и отправляет файл клиенту.Сервер, похоже, отправляет все данные, но я проверил на стороне клиента, что полученные байты! = Отправленные байты.

После дальнейшего изучения клиентская сторона метода приема указала, что сервер закрывает соединениена раннем этапе, что приводит к сбою приема.

Итак, я изменил сервер на пару секунд после отправки файла - оставив сокет открытым достаточно долго, чтобы клиент получил его, а затем закрыв его.Это работает, но, на мой взгляд, очень хакерски, потому что трудно предсказать, как долго поток должен спать до закрытия сокета.

Я попытался установить сторону сервера SO_LINGER, чтобы поддерживать соединение, но это не такпомощь - хотя я думаю, что так и должно быть.

Должен быть лучший способ блокировки, пока клиент полностью не получит файл.Что мне нужно сделать, чтобы гарантировать, что сокет не закроется, пока клиент не получит все данные?

Сервер

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)

Клиент, подключающийся кСервер

    // 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;
    }

Клиент, получающий

    // 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;
    }

Ответы [ 2 ]

1 голос
/ 20 января 2014

Я пропускаю вашу часть кода. Я просто сосредоточусь на вашей проблеме, получая полный файл. Один классный способ - адаптировать HTTP. Сначала просто отправьте количество байтов, которое вы собираетесь отправить, чтобы получатель получал от сокета только до этой суммы. Таким образом, отправка небольшого количества дополнительных данных получателю сделает эту работу ..

Отправитель:

  • Отправить размер файла для отправки
  • Отправить данные файла

приемник:

  • Размер получаемого файла
  • Получать данные до тех пор, пока len (data) == size_received
0 голосов
/ 04 апреля 2011

Я не могу понять, почему сон что-то решил.

Но я думаю, что python при отправке 5 байтов, а C ++ читает 8 байтов. ? в python занимает байты. BOOL Я считаю, что typedefed как int и занимает, вероятно, 4 байта.

В общем случае чтение / запись структур в сокеты не рекомендуется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...