QTcpSocket и TCP padding

Добрый день.

Я отправляю собственный протокол для регистрации через TCP, который выглядит следующим образом:

Timestamp (uint32_t -> 4 bytes)
Length of message (uint8_t -> 1 byte)
Message (char -> Length of message)

Временная метка преобразуется в BigEndian для транспорта и все идет правильно, за исключением одной маленькой детали: обивка

Отметка времени отправляется сама по себе, однако вместо простой отправки отметки времени (4 байта) мое приложение (используя сокеты BSD в Ubuntu) автоматически добавляет к сообщению два байта заполнения.

Wireshark распознает это правильно и помечает два посторонних байта как заполнение, однако QTcpSocket (Qt 5.8, mingw 5.3.0), очевидно, предполагает, что два дополнительных байта на самом деле являются полезной нагрузкой, что, очевидно, портит мой протокол.

Можно ли как-нибудь научить QTcpSocket игнорировать заполнение (как это должно быть) или каким-либо образом избавиться от заполнения?

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

Большое спасибо.

Поскольку его спросили, код, используемый для отправки данных:

return 
    C->sendInt(entry.TS)        &&
    C->send(&entry.LogLen, 1)   &&
    C->send(&entry.LogMsg, entry.LogLen);

где sendInt объявлен как (Src являясь параметром):

Src = htonl(Src);
return send(&Src, 4);

где 'send' объявлен как (Source а также Len являясь параметрами):

char *Src = (char *)Source;
while(Len) {
    int BCount = ::send(Sock, Src, Len, 0);
    if(BCount < 1) return false;
    Src     += BCount;
    Len     -= BCount;
}
return true;

:: send это стандартная функция отправки BSD.

Чтение осуществляется через QTcpSocket:

uint32_t timestamp;
if (Sock.read((char *)&timestamp, sizeof(timestamp)) > 0)
{
    uint8_t logLen;
    char message[256];            
    if (Sock.read((char *)&logLen, sizeof(logLen)) > 0  &&
        logLen > 0                                      &&
        Sock.read(message, logLen) == logLen
    ) addToLog(qFromBigEndian(timestamp), message);
}

Sock это QTcpSocket экземпляр, уже подключенный к хосту и addToLog это функция обработки.

Также следует отметить, что отправляющая сторона должна работать во встроенной системе, используя QTcpServer для этого не вариант.

3 ответа

Решение

Ваша логика чтения неверна. У тебя есть...

uint32_t timestamp;
if (Sock.read((char *)&timestamp, sizeof(timestamp)) > 0)
{
    uint8_t logLen;
    char message[256];            
    if (Sock.read((char *)&logLen, sizeof(logLen)) > 0  &&
        logLen > 0                                      &&
        Sock.read(message, logLen) == logLen
    ) addToLog(qFromBigEndian(timestamp), message);
}

Из документации для QTcpSocket::read(data, MaxSize) Это...

Читает не более maxSize байтов с устройства в данные и возвращает количество прочитанных байтов

Что делать, если один из ваших звонков Sock.read читает частичные данные? По сути, вы отбрасываете эти данные, а не буферизуете для повторного использования в следующий раз.

Если у вас есть подходящая область QByteArray...

QByteArray data;

Ваша логика чтения должна быть больше похожа на...

/*
 * Append all available data to `data'.
 */
data.append(Sock.readAll());

/*
 * Now repeatedly read/trim messages from data until
 * we have no further complete messages.
 */
while (contains_complete_log_message(data)) {
  auto message = read_message_from_data(data);
  data = data.right(data.size() - message.size());
}

/*
 * At this point `data' may be non-empty but doesn't
 * contain enough data for a complete message.
 */

Если длина отступа всегда фиксирована, просто добавьте socket->read(2); игнорировать 2 байта.

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

Вы не должны вызывать отправить три раза, но только один раз. Для преобразования в BigEndian вы можете использовать функции Qt и записывать все в один буфер и вызывать send только один раз. Это не то, что вы хотите, но я предполагаю, что это то, что вам нужно сделать, и это должно быть легко, так как вы уже знаете размер своего сообщения. Вам также не нужно покидать мир Qt для отправки сообщений.

Другие вопросы по тегам