read(sock...) и write(sock...) не работают синхронно

read(sock...) и write(sock...) не работают синхронно

11.04.2017 04:54:38 Просмотров 29 Источник

ОТВЕТ : Ошибку нашел. В методе Client::download() поменял строчку записи в файл с ofs<<buffer; на ofs.write(buffer, nBytesReceived); В первом случае запись в файл продолжалась только до первого нулевого символа в пачке данных, а во втором случае я явно указал, чтобы в файл записывалось именно nBytesReceived байт.

Касательно зависания сервера: дело было в том, что у меня в функции отправки было write(sock, buf, strlen(buf));. Естественно, что когда встречался нулевой символ, которых достаточно в исполняемых файлах, то strlen() был совсем не 1024 байт. В функции чтения у меня было read(sock, buf, 1024). Видимо, так работает передача, что если пакет оказывался меньше 1024 байт (такой размер мы ждали), то чтение тупило.

C++, Ununtu 16.04 LTS, Компилятор g++

Всем доброго времени. Пишу клиент-серверное приложение. Задача довольно простая : передать файл от сервера клиенту. Отправляю клиенту размер файла, делю этот размер на 1024, чтобы получить количество пакетов, которое я должен получить (я отправляю по 1024 байта). С этой частью все в порядке. Когда начинается работа циклов for и while, то тут я сталкиваюсь с проблемой, что сервер быстренько обрабатывает все свои write, заталкивает весь файл в буфер и ждет. Клиент же читает почему-то только первые сколько-нибудь пакетов и потом останавливается на какой-то итерации. Далее - когда я в окне с сервером прерываю серверный процесс (ctrl+C на Ubuntu), то клиент сразу докачивает остальное до конца. Я не устанавливал неблокирующие сокеты. Не понимаю, в чем дело. Объясните, пожалуйста, если в курсе.

Клиент :

void Client::download(std::string filename)
{
    std::ofstream ofs(filename, std::ofstream::app);
    int nBytesReceived;
    int nFileLength = 0;
    int nPackages = 0;
    char buffer[1024];
    memset(buffer, '\0', sizeof(buffer));

    //Получение длины файла
    nBytesReceived = read(sockfd, buffer, sizeof(buffer));
    if (nBytesReceived < 0) 
        error("ERROR reading from socket");

    //Length of the file
    nFileLength = atoi(buffer);
    nPackages = nFileLength % BUF > 0 
    ? nFileLength / BUF + 1
    : nFileLength / BUF;

    //Тут происходит передача файла
    for(int i = 0; i<nPackages; ++i){
        //Read from the socket
        nBytesReceived = read(sockfd, buffer, sizeof(buffer));
        if (nBytesReceived < 0) 
            error("ERROR reading from socket");

        ofs<<buffer;
    }
    ofs.close();
}

Сервер :

void Server::upload(std::string filename, int nFilePos){

    std::ifstream ifs(filename, std::ifstream::in);
    char buffer[BUF];
    memset(buffer, 0, sizeof(buffer));

    ifs.seekg(0, ifs.end);
    int nFileLength = ifs.tellg();

    //Тут происходит отправка длины файла
    nBytesSent = write(newsockfd, std::to_string(nFileLength).c_str()
    , strlen(std::to_string(nFileLength).c_str()));
    if (nBytesSent < 0) 
        error("ERROR writing to pipe");    
    this->send();
    ifs.seekg(0, ifs.beg);

    //Тут происходит передача файла
    while (!ifs.eof()){
        ifs.read(buffer, BUF);
        nBytesSent = write(newsockfd, buffer, strlen(buffer));
        if (nBytesSent < 0) 
            error("ERROR writing to pipe");     
    }
    ifs.close();
}
У вопроса есть решение - Посмотреть?

https://ru.stackoverflow.com/questions/652426/readsock-%d0%b8-writesock-%d0%bd%d0%b5-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d1%8e%d1%82-%d1%81%d0%b8%d0%bd%d1%85%d1%80%d0%be%d0%bd%d0%bd%d0%be#comment933870_652426
Почему вы считаете, что read всегда читает буфер целиком? И почему вы думаете, что в файле нет нулевых байт внутри?
https://ru.stackoverflow.com/questions/652426/readsock-%d0%b8-writesock-%d0%bd%d0%b5-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d1%8e%d1%82-%d1%81%d0%b8%d0%bd%d1%85%d1%80%d0%be%d0%bd%d0%bd%d0%be#comment933876_652426
read для получения длины файла в клиенте запросто может прочесть часть первого блока передаваемого файла. Правда, это не объясняет "зависания" с последующим дочитыванием.
https://ru.stackoverflow.com/questions/652426/readsock-%d0%b8-writesock-%d0%bd%d0%b5-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d1%8e%d1%82-%d1%81%d0%b8%d0%bd%d1%85%d1%80%d0%be%d0%bd%d0%bd%d0%be#comment933887_652426
@avp а кстати объясняет :) Если часть данных была прочитана вместе с длиной в начале (хорошо если передаваемый файл не начинался на цифры и atoi их не захватит). то при чтении в цикле фиксированного количества блоков данных в потоке не хватает и цикл висит на последнем read ожидая еще данных. А когда сервер завершается, соединение закрывается и клиента отпускает
https://ru.stackoverflow.com/questions/652426/readsock-%d0%b8-writesock-%d0%bd%d0%b5-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d1%8e%d1%82-%d1%81%d0%b8%d0%bd%d1%85%d1%80%d0%be%d0%bd%d0%bd%d0%be#comment933935_652426
Друзья, размер читается корректно. Тут я просто удалил всякие лишние переменные отладочные. Число передается корректно. Допустим, что у нас получилась длина на 2000 пакетов по 1024байта. В сервере все 2000 итераций происходят , а в клиенте только 500 и он останавливается (количество итераций я вывожу на экран). Когда я закрываю сервер, то клиент тут же производит остальные итерации и тоже выводит на экран 2000. Файл перекачивается до конца. Правда, там еще какой-то мусор попадается (подозреваю, что это лишние /r/n символы), поэтому файл битый получается. Но по размерам почти одинаков.
https://ru.stackoverflow.com/questions/652426/readsock-%d0%b8-writesock-%d0%bd%d0%b5-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d1%8e%d1%82-%d1%81%d0%b8%d0%bd%d1%85%d1%80%d0%be%d0%bd%d0%bd%d0%be#comment933942_652426
@LigvestO То что размер считался корректно OK. А сколько байт при этом вернул первый read ? вы напечатайте сразу как только прочитали размер nBytesReceived там действительно длина строки ? В любом случае сокеты TCP не работают пакетами как вы отдаете, а передают по сети как им удобнее и с этим надо считаться. И клиент должен знать сколько байт ему передал сервер в первом пакете. А для этого надо, например, передавать длину в виде фиксированного количества байт.
https://ru.stackoverflow.com/questions/652426/readsock-%d0%b8-writesock-%d0%bd%d0%b5-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d1%8e%d1%82-%d1%81%d0%b8%d0%bd%d1%85%d1%80%d0%be%d0%bd%d0%bd%d0%be#comment933943_652426
Я еще тут подумал : если сервер все отправляет, а клиент где-то зависает, то значит, что мне надо искать проблему в клиенте? Сейчас гляну количество передаваемых байт.
https://ru.stackoverflow.com/questions/652426/readsock-%d0%b8-writesock-%d0%bd%d0%b5-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d1%8e%d1%82-%d1%81%d0%b8%d0%bd%d1%85%d1%80%d0%be%d0%bd%d0%bd%d0%be#comment933944_652426
@LigvestO И зачем кстати эти усложнения с получением длины в виде строки и передачей строкой. прямо передайте переменную nFileLength в виде int и читайте ровно sizeof(int) байт (конечно с учетом, что на другой архитекутре размер int может отличаться и быть с другим порядком байт)
https://ru.stackoverflow.com/questions/652426/readsock-%d0%b8-writesock-%d0%bd%d0%b5-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d1%8e%d1%82-%d1%81%d0%b8%d0%bd%d1%85%d1%80%d0%be%d0%bd%d0%bd%d0%be#comment934227_652426
@Mike, вот из-за проблем с представлением лучше размер передать строкой. НО, строкой фиксированного размера, скажем 16 байт вместе с завершающим нулем.
https://ru.stackoverflow.com/questions/652426/readsock-%d0%b8-writesock-%d0%bd%d0%b5-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d1%8e%d1%82-%d1%81%d0%b8%d0%bd%d1%85%d1%80%d0%be%d0%bd%d0%bd%d0%be#comment934371_652426
@avp Ну htonl/ntohl то же в принципе никто не отменял
https://ru.stackoverflow.com/questions/652426/readsock-%d0%b8-writesock-%d0%bd%d0%b5-%d1%80%d0%b0%d0%b1%d0%be%d1%82%d0%b0%d1%8e%d1%82-%d1%81%d0%b8%d0%bd%d1%85%d1%80%d0%be%d0%bd%d0%bd%d0%be#comment934475_652426
Совет передавать int очень хороший. Сейчас так и делаю. Отправляется 4 байта и получается столько же. Добавил еще memset() перед отправкой и перед получением для буферов. Попробовал передать .txt файл на 2.3кб, все получилось. 3 блока : первые два по 1024б и последний на 290б. Попробовал на .odt(аналог .doc) и тут какая-то фигня. На сервере отправка вообще какая-то сумбурная : 5, 460, 89, 0, 77, 121 и т.д. байт. На клиенте получил 1024, 13, а дальше по ноль байт. Пытаюсь пока разобраться. Разбираюсь дальше пока.

Ответы - read(sock...) и write(sock...) не работают синхронно / read(sock...) и write(sock...) не работают синхронно

Sergey

13.04.2017 11:13:36

Ваш сервер пишет пакеты длиной

strlen(buffer)

А клиент читает пакет длиной

sizeof(buffer)

Дальше два варианта:

  1. Ваш sockfd - это TCP. Если в буфере есть хоть один байт \0, то вся передача "разъезжается"...
  2. Ваш sockfd - это UDP - фактическую длину принятого блока надо самому проверять.
Закрыть X