Почему я не получаю все байты изображения на сервере? - PullRequest
0 голосов
/ 13 ноября 2018

Я создаю приложение для iOS, которое делает фотографию и отправляет ее на TCP-сервер, работающий на моем компьютере.Для этого я настраиваю соединение с потоками следующим образом:

func setupCommunication() {
    var readStream: Unmanaged<CFReadStream>?
    var writeStream: Unmanaged<CFWriteStream>?

    CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
        "192.168.1.40" as CFString, 2323, &readStream, &writeStream)

    outputStream = writeStream!.takeRetainedValue()
    outputStream.schedule(in: .current, forMode: .common)
    outputStream.open()
}

Затем, когда я нажимаю кнопку камеры, фотография берется и отправляется через outputStream.Поскольку TCP-сервер не знает, сколько данных он должен прочитать, первые 8 байтов соответствуют размеру изображения, и изображение отправляется сразу после, как мы можем видеть в этом коде:

func photoOutput(_ output: AVCapturePhotoOutput,
    didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {

    if let image = photo.fileDataRepresentation() {
        print(image)
        print(image.count)

        var nBytes = UInt64(image.count)
        let nData = Data(bytes: &nBytes, count: 8)
        _ = nData.withUnsafeBytes({
            outputStream.write($0, maxLength: nData.count)
        })

        _ = image.withUnsafeBytes({
            outputStream.write($0, maxLength: image.count)
        })

        outputStream.close()
    }
}

На стороне сервера, которая написана на C, я выполняю следующие действия:

Считайте первые 8 байтов, чтобы узнать размер изображения

printf("\n[*] New client connected\n");

while (n_recv < sizeof(uint64_t)) {
    if ((n = read(client_sd, buffer, BUF_SIZ)) == -1) {
        printf("\n[-] Error reading data from the client\n");
        close(client_sd);
        close(server_sd);
        return 0;
    }

    n_recv += n;
}

memcpy(&img_size, buffer, sizeof(uint64_t));

printf("\n[+] Client says he's going to send %llu bytes\n", img_size);

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

if ((img_data = (uint8_t *) malloc(img_size)) == NULL) {
    printf("\n[-] Error allocating memory for image\n");
    close(client_sd);
    close(server_sd);
    return 0;
}

n_recv -= sizeof(uint64_t);
if (n_recv > 0) {
    memcpy(img_data, buffer, n_recv);
}

С этого момента n_recv - это число байт, полученных только для изображения, а невключая первые 8 байтов для размера.Тогда просто читайте до конца.

while (n_recv < img_size) {
    if ((n = read(client_sd, buffer, BUF_SIZ)) == -1) {
        printf("\n[-] Error reading data from the client\n");
        close(client_sd);
        close(server_sd);
        return 0;
    }

    memcpy(img_data + n_recv, buffer, n);
    n_recv += n;
}

printf("\n[+] Data correctly recived from client\n");

close(client_sd);
close(server_sd);

В начале это работает довольно хорошо.Фактически, я вижу, что каждый раз получаю правильное число для размера изображения:

enter image description here

Однако я не получаюполное изображение, и сервер просто продолжает ждать заблокирован в функции чтения.Чтобы увидеть, что происходит, я добавил это

printf("%llu\n", n_recv);

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

enter image description here

В чем проблема, вызывающая связь сстоп?Проблема в коде сервера или это связано с приложением iOS?

1 Ответ

0 голосов
/ 13 ноября 2018

Во-первых, код C выглядит нормально для меня ... но вы понимаете, что вам не хватает кода возврата / обработки результатов в Swift?

В коде C вы проверяете возвращаемое значение recv, чтобы узнать, были ли прочитаны байты. IE: Вы проверяете, возвращает ли read -1 ..

Однако в быстром коде вы предполагаете, что ВСЕ данные были записаны. Вы никогда не проверяли результат операции write на OutputStream, которая сообщает вам, сколько байтов было записано, или возвращает -1 при сбое. ..

Вы должны делать то же самое (в конце концов, вы сделали это в C) .. Для таких случаев я создал два расширения:

extension InputStream {
    /**
     * Reads from the stream into a data buffer.
     * Returns the count of the amount of bytes read from the stream.
     * Returns -1 if reading fails or an error has occurred on the stream.
     **/
    func read(data: inout Data) -> Int {
        let bufferSize = 1024
        var totalBytesRead = 0

        while true {
            let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
            let count = read(buffer, maxLength: bufferSize)
            if count == 0 {
                return totalBytesRead
            }

            if count == -1 {
                if let streamError = self.streamError {
                    debugPrint("Stream Error: \(String(describing: streamError))")
                }
                return -1
            }

            data.append(buffer, count: count)
            totalBytesRead += count
        }
        return totalBytesRead
    }
}

extension OutputStream {
    /**
     * Writes from a buffer into the stream.
     * Returns the count of the amount of bytes written to the stream.
     * Returns -1 if writing fails or an error has occurred on the stream.
     **/
    func write(data: Data) -> Int {
        var bytesRemaining = data.count
        var bytesWritten = 0

        while bytesRemaining > 0 {
            let count = data.withUnsafeBytes {
                self.write($0.advanced(by: bytesWritten), maxLength: bytesRemaining)
            }

            if count == 0 {
                return bytesWritten
            }

            if count < 0 {
                if let streamError = self.streamError {
                    debugPrint("Stream Error: \(String(describing: streamError))")
                }
                return -1
            }

            bytesRemaining -= count
            bytesWritten += count
        }

        return bytesWritten
    }
}

Использование:

var readStream: Unmanaged<CFReadStream>?
var writeStream: Unmanaged<CFWriteStream>?

//For testing I used 127.0.0.1
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, "192.168.1.40" as CFString, 2323, &readStream, &writeStream)


//Actually not sure if these need to be retained or unretained might be fine..
//Again, not sure..
var inputStream = readStream!.takeRetainedValue() as InputStream
var outputStream = writeStream!.takeRetainedValue() as OutputStream

inputStream.schedule(in: .current, forMode: .common)
outputStream.schedule(in: .current, forMode: .common)

inputStream.open()
outputStream.open()


var dataToWrite = Data() //Your Image
var dataRead = Data(capacity: 256) //Server response -- Pre-Allocate something large enough that you "think" you might read..

outputStream.write(data: dataToWrite)
inputStream.read(data: &dataRead)

Теперь вы получаете обработку ошибок (печать) и буферизуете чтение / запись .. В конце концов, вы не гарантированы, что сокет, канал или w / e .. поток, к которому присоединен поток, прочитал / записал ВСЕ ваши байт сразу .. отсюда и фрагменты чтения / записи.

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