Проблема с отправкой данных по UDP с использованием fread - PullRequest
0 голосов
/ 19 февраля 2019

Я работаю над реализацией протокола "остановись и работай" с использованием UDP.Я по существу использую буфер символов для сериализации любых данных, которые я посылаю, используя ntoh и hton, где это необходимо, и десериализацию, как только мои данные достигают другой стороны.Все отлично работает, однако, когда я пытаюсь отправить файл, я получаю случайные фиктивные байты в новом файле.Я попытаюсь обобщить необходимую информацию для этого вопроса:

Первый символ в моем буфере символов представляет тип отправляемого пакета.Я установил небольшое трехстороннее рукопожатие для установления соединения.Сервер просто принимает определенный порт в качестве аргумента, клиент берет IP-адрес, порт и имя файла, а затем запрашивает этот файл с сервера.Сервер отвечает размером файла, а затем клиент отправляет окончательный пакет ACK, после чего данные отправляются.Я использую fread () для отправки данных ~ 1 КБ за раз, используя размер буфера 1 КБ.Как только клиент получает этот пакет, он отправляет пакет подтверждения, а затем ожидает следующий пакет данных.Вот соответствующий код:

                else if (b == 'a')
                {
                        memcpy(&dacpkt.seqNum, buffer+sizeof(char), sizeof(int));
                        dacpkt.seqNum = ntohl(dacpkt.seqNum);           // convert from network to host data
                        datapkt.pktLen = 1017;
                        if (dacpkt.seqNum == datapkt.seqNum)
                        {
                                ++seq;
                                dacpkt.t = b;

                                datapkt.seqNum = seq;
                                fread(datapkt.data, datapkt.pktLen, 1, filereq);

                                memcpy(buffer, &datapkt.t, sizeof(char));                               // data packet type
                                off = sizeof(char);
                                datapkt.seqNum = htonl(datapkt.seqNum);
                                memcpy(buffer + off, &datapkt.seqNum, sizeof(int));     // data packet sequence#
                                off += sizeof(int);
                                datapkt.pktLen = htons(datapkt.pktLen);
                                memcpy(buffer + off, &datapkt.pktLen, sizeof(short));   // data packet size
                                off += sizeof(short);
                                memcpy(buffer + off, &datapkt.data, 1017);                              // data packet payload
                                n = sendto(sock, buffer, MAX, 0, (struct sockaddr*) &from, fromlen);
                                if (n < 0)
                                        error("sendto");
                        }

                        if(fread(datapkt.data, 1017, 1, filereq) != 1017)       // fread hit the end of the file
                        {
                                ++seq;
                                dacpkt.t = 'c';
                                dacpkt.seqNum = seq;
                                memcpy(buffer, &dacpkt.t, sizeof(char));                        // close packet
                                off = sizeof(char);
                                dacpkt.seqNum = htonl(dacpkt.seqNum);
                                memcpy(buffer + off, &dacpkt.seqNum, sizeof(int));      // close packet sequence#
                                n = sendto(sock, buffer, 5, 0, (struct sockaddr*) &from, fromlen);
                        }

                }

dacpkt - это пакет подтверждения, который я заполняю при десериализации буфера символов из recvfrom (), а datapkt - это пакет, который я строю, затем сериализую и отправляю.Я СЧИТАЮ, что моя проблема в моем использовании фреда.Вот код получения на стороне клиента:

                        if (b == 'd')
                        {
                                //build the response packet and send
                                memset(buffer, 0, MAX);
                                //populate struct
                                dacpkt.t = 'a';
                                dacpkt.seqNum = htonl(sequence_num);

                                //copy to buffer
                                memcpy(buffer, &dacpkt.t, sizeof(char));
                                off = sizeof(char);
                                memcpy(buffer + off, &dacpkt.seqNum, sizeof(int));

                                //send the ACK
                                n = sendto(sock, buffer, MAX, 0, (struct sockaddr *) &server, length);
                                if (n < 0)
                                        error("sendto");

                                //receive as well before exiting while loop
                                memset(buffer, 0, MAX);
                                n = recvfrom(sock, buffer, MAX, 0, (struct sockaddr *) &from, &length);
                                if (n < 0)
                                        error("recvfrom");

                                datapkt.t = buffer[0];
                                off = sizeof(char);
                                memcpy(&datapkt.seqNum, buffer + off, sizeof(int));
                                datapkt.seqNum = ntohl(datapkt.seqNum);
                                off += sizeof(int);
                                memcpy(&datapkt.pktLen, buffer + off, sizeof(short));
                                datapkt.pktLen = ntohl(datapkt.pktLen);
                                off += sizeof(short);
                                strcat(datapkt.data, buffer + off);

                                //ensures we are receiving the next packet
                                if (datapkt.seqNum = (sequence_num + 1))
                                {
                                        n = fputs(datapkt.data, newfile); //we need buffer to be the exact size
                                        if (n < 0)
                                                error("writing to file");

                                        sequence_num = datapkt.seqNum;
                                }

                        }

                        fclose(newfile);
                }
                //post-receiving the file!
                memset(buffer, 0, MAX);
                dacpkt.t = 'c';
                dacpkt.seqNum = htonl(sequence_num);
                memcpy(buffer, &dacpkt.t, sizeof(char));
                memcpy(buffer + 1, &dacpkt.seqNum, sizeof(int));
                //send the ACK
                n = sendto(sock, buffer, MAX, 0, (struct sockaddr *) &server, length);
                if (n < 0)
                        error("sendto");

Извините, если информация, которую я предоставил, немного неоднозначна, но я вполне уверен, что мои проблемы заключаются в чтении / записи в файл.Например, я запустил программу, пытаясь передать текстовый файл с алфавитными символами (az) в каждой строке, на 600 строк.Исходный файл имел размер 16200 байт, а новый файл, созданный отправляющими данными, - всего 3069 байт.Новый файл содержит всего 113 строк, и случайные байты появляются повсюду так:

 36 abcdefghijklmnopqrstuvwxyz
 37 abcdefghijklmnopqrstuvwxyz
 38 abcdefghijklmnopqr8ze$ü^?abcdefghijklmnopqrstuvwxyz
 39 abcdefghijklmnopqrstuvwxyz

и, в частности, в конце файла:

111 abcdefghijklmnopqrstuvwxyz
112 abcdefghijklmnopqrstuvwxyz
113 abcdefghi8ze$ü^?

Я думаю, что я не понимаюкак работает fread () в отношении того, как определить, когда закончить передачу файла.Если у кого-то есть идеи о том, как я мог бы реализовать это лучше, я весь в ушах.Все, что я хочу сделать, это отправлять данные по одному килобайтному пакету за раз, пока я не достигну конца файла, после чего я отправляю этот окончательный частичный пакет, а затем отправляю сигнал на закрытие клиента.

1 Ответ

0 голосов
/ 19 февраля 2019

Две вещи поражают сразу.

  • fread возвращает количество элементов , которые он прочитал.fread(datapkt.data, 1017, 1, filereq) пытается прочитать один элемент размером 1017. Нет никаких шансов, что этот вызов когда-либо вернет 1017. Он может вернуть только 0 или 1. Тест

        if (fread(....) != 1017)
    

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

    Поменяйте местами порядок параметров, проверьте, как могут быть прочитаны 1-байтовые элементы:

        size_t bytes = fread(datapkt.data, 1, 1017, filereq);
    

    и используйте этот bytes в качестве размераполезная нагрузка.bytes < 1017 может указывать на конец файла.Чтобы быть уверенным, звоните feof и ferror.

  • Поток сервера:

        if (dacpkt.seqNum == datapkt.seqNum) {
            ....
            fread(...);
            ....
        }
        if (fread(....) != 1017) {
            ....
        }
    

    означает, что есть два вызована fread один раз seqNum совпадение и один вызов, если это не так.Я не могу следить за потоком данных, но это странно.Сервер не должен отправлять новые seqNum до получения подтверждения.

    В любом случае он не должен слепо отправлять MAX байтов.Первый fread также должен проверить, сколько байтов он получил, и зафиксировать размеры в memcpy и sendto соответственно.


Для _Etect EOF и чтения данныходновременно попробуйте что-то вроде

    size_t bytes = fread(datapkt.data, 1, 1017, filereq);
    if (bytes == 0 && feof(filereq)) {
        handle_end_of_file();
    } else {
        memcpy(memcpy(buffer + off, &datapkt.data, bytes);
        send_the_packet();
    }
...