Сохранить данные из `recvfrom()` в структуру, чтобы избежать лишних байтов?

#c

Вопрос:

Я получаю пакеты по upd и создаю структуру PSG. Затем я сохраняю его в вектор и сортирую.В конце я записываю все байтовые данные в файл. Проблема в том, что последний пакет составляет менее 1424 байт. и из-за этого в конец файла записываются дополнительные байты. Как я могу правильно сохранить данные из recvfrom() структуры, чтобы избежать лишних байтов?

 #pragma pack(push, 1)
    struct PSG
    {
        uint64_t id;
        uint64_t size;
        uint32_t type;
        uint32_t count;
        uint8_t data[1400];
    };
    #pragma pack(pop)


PSG psg;
std::vector<PSG> psg_vector;
 while(1) {

        if ((bytesrecv = recvfrom(m_sock, amp;psg, 1424, 0, (sockaddr *) NULL, NULL)) < 0) {
            perror("recvfrom");
            close(m_sock);
            return -1;
        }
       psg_vector.push_back(psg);

       sort(psg_vector.begin(), psg_vector.end(), [](const auto amp;lhs, const 
            auto amp;rhs) {
            return lhs.count < rhs.count;
        });
       for (auto amp;a: psg_vector) {
           file.write(reinterpret_cast<const char *>(amp;a.data), sizeof(a.data));
}

}
 

Комментарии:

1. Может быть , использовать что-то другое, чем sizeof(a.data) , может PSG::count быть ?

2. Вместо передачи sizeof(a.данные) в файл. write(), вычислите фактическое количество байтов, которые вы хотите записать, и передайте их.

3. с справочной страницы: recvfrom возвращает «…количество полученных байтов или -1, если произошла ошибка. Возвращаемое значение будет равно 0, когда одноранговый узел выполнит упорядоченное завершение работы.». Используется bytesrecv для проверки ошибок и определения количества байтов для записи в файл.

Ответ №1:

Вы получаете размер считанного пакета bytesrecv , но затем игнорируете его и не используете его. Храните его где-нибудь и используйте. Вы можете добавить его в свой объект PSG:

 #pragma pack(push, 1)
    struct PSG
    {
        uint64_t id;
        uint64_t size;
        uint32_t type;
        uint32_t count;
        uint8_t data[1400];
        ssize_t size;
    };
    #pragma pack(pop)


PSG psg;
std::vector<PSG> psg_vector;
 while(1) {

        if ((psg.size = recvfrom(m_sock, amp;psg, 1424, 0, (sockaddr *) NULL, NULL)) < 0) {
            perror("recvfrom");
            close(m_sock);
            return -1;
        }
       psg_vector.push_back(psg);
 

Ответ №2:

Пример на сокетах linux. Вам нужны эти строки:

 if (!length)  // Write entire buffer
   fileManager.WriteFile(data->buffer, BUFF_LENGTH);
else  // Write remain
{
   fileManager.WriteFile(data->buffer, length);
   break;
}
 

Полный пример:

 struct Packet
{
     enum class TYPE
     {
        DEFAULT = 3,  // Default condition
        LAST_OF_FILE = 4,  // Last packet of file
        N_LAST_OF_FILE = 5,  // Not last packet of file
        LAST_IN_GROUP = 6,  // Last file in group of files, sent by client
        N_LAST_IN_GROUP = 7,  // Not last file in group of files, sent by client
        ANSWER = 8  // Answer
     };

    uint32_t length;
    TYPE type;
    char buffer[BUFF_LENGTH];

    void clear()
    {
        memset(this, 0, sizeof(Packet));
    }
};


bool ReceiveOnePacket(Packet* pack, int* socket)
{
    // summary quantity of received bytes
    uint32_t bytes = 0;

    while (bytes < sizeof(Packet))
    {
        // a tcp socket cannot give the entire packet as a whole, so we read it in parts.
        // We read with a shift and reduce the number of remaining bytes
        int len = recv(*socket, ((char*)pack)   bytes, sizeof(Packet) - bytes, 0);

        if (len <= 0)
            return false;

        bytes  = len;
    }
    return true;
}

void ReceivingFiles(std::shared_ptr<int> socket, std::shared_ptr<Packet> data)
{
    data->clear();
    bool isLastFile = false;

    while (!isLastFile)  // Receiving set of files
    {
        // receiving filename of current file
        if (!ReceiveOnePacket(data.get(), socket.get()))
        {
            close(*socket);
            return;
        }

        // if current file is last then data->packetType == Packet::TYPE::LAST_IN_GROUP,
        // if file not last, data->packetType == Packet::TYPE::N_LAST_IN_GROUP
        isLastFile = data->type == Packet::TYPE::LAST_IN_GROUP;

        std::string metaData(data->buffer, data->length);

        fileManager.OpenFile(metaData);

        // Receiving one File
        while (true)
        {
            if (!ReceiveOnePacket(data.get(), socket.get()))
            {
                fileManager.DeleteFile();
                close(*socket);
                return;
            }

            uint32_t length = data->length;

            if (!length)  // Write entire buffer
                fileManager.WriteFile(data->buffer, BUFF_LENGTH);
            else  // Write remain
            {
                fileManager.WriteFile(data->buffer, length);
                break;
            }

        }  // End Receiving one file
    }
}