Delphi C++ Indy UDP-сервер BytesToString не получает правильную строку?

Я пытаюсь получить UDP-пакеты от мульти-симулятора TUIO. Но некоторые данные отсутствуют.

https://www.tuio.org/?specification

void __fastcall TMain::UDPServerUDPRead(TIdUDPListenerThread *AThread, const TIdBytes AData,
    TIdSocketHandle *ABinding)
{
    UDPData->Lines->Add(BytesToString(AData));

    for (int i = 0; i < AData.Length; i++)
        UDPData->Lines->Add((char)AData[i]);
}

Когда я использую BytesToString(), я получаю только слово "#bundle" в виде строки, но когда я использую второй цикл, я получаю следующее:

# б ты п d л е

/ т ты я о / 2 D c ты р , s я а л я v е 4 / т ты я о / 2 D c ты р , s я ж ж ж ж ж s е т ?

ホ 9 >  ᅳ

/ т ты я о / 2 D c ты р , s я ж s е q # б ты п d л е

/ т ты я о / 2 D о б j , s а л я v е

/ т ты я о / 2 D о б j , s я ж s е q # б ты п d л е

/ т ты я о / 2 D c ты р , s а л я v е

/ т ты я о / 2 D c ты р , s я ж s е q

Конечно, с каждым персонажем в отдельной строке.

Итак, как мне получить правильные данные, если я не знаю кодировку? Я потерялся здесь и действительно нуждаюсь в помощи.

Кроме того, как я могу отправить аналогичные данные пакета с TIdUDPClient?

Итак, в какой кодировке отправляются данные? И почему вы обрабатываете его по одному байту? А как узнать, что пришла вся посылка?

David Heffernan 13.06.2018 00:05

@DavidHeffernan "как узнать, что вся посылка прибыла?" - UDP ориентирован на сообщения, а не на поток, как TCP. TIdUDPServer принимает только целые сообщения, а не частичные сообщения.

Remy Lebeau 13.06.2018 00:28
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
488
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Показанные вами данные UDP - это в основном двоичные данные с некоторыми текстовыми элементами внутри. Таким образом, вам не следует преобразовывать весьAData в одну строку с BytesToString(), поскольку это не совсем текст для начала (ну, вы могли бы, используя BytesToStringRaw() или BytesToString() с IndyTextEncoding_8bit() в качестве кодировки, но на самом деле это не так. очень помогу вам в этом).

Вам необходимо разбить данные на отдельные компоненты в соответствии с спецификация протокола, с которым вы связались (который, в свою очередь, основан на протоколе Открыть управление звуком). Только тогда вы сможете обрабатывать компоненты по мере необходимости. Это означает, что вам нужно перебрать AData побайтно, анализируя каждый байт в контексте согласно протоколу.

Например (это далеко не полная реализация, но она должна дать вам представление о том, какая логика задействована):

struct OSCArgument
{
    char Type;
    Variant Data;
}

struct OSCBundleElement
{
    virtual ~OSCBundleElement() {}
};

struct OSCMessage : OSCBundleElement
{
    String AddressPattern;
    DynamicArray<OSCArgument> Arguments;
};

struct OSCBundle : OSCBundleElement
{
    TDateTime TimeTag;
    DynamicArray<OSCBundleElement*> Elements;

    ~OSCBundle()
    {
        int len = Elements.Length;
        for (int i = 0; i < len; ++i)
            delete Elements[i];
    }
};

int32_t readInt32(const TIdBytes &Bytes, int &offset)
{
    uint32_t ret = GStack->NetworkToHost(BytesToUInt32(Bytes, offset));
    offet += 4;
    return reinterpret_cast<int32_t&>(ret);
}

TDateTime readOSCTimeTag(const TIdBytes &Bytes, int &offset)
{
    int32_t secondsSinceEpoch = readInt32(Bytes, offset); // since January 1 1900 00:00:00
    int32_t fractionalSeconds = readInt32(Bytes, offset); // precision of about 200 picoseconds
    // TODO: convert seconds to TDateTime...
    TDateTime ret = ...;
    return ret;
}

float readFloat32(const TIdBytes &Bytes, int &offset)
{
    uint32_t ret = BytesToUInt32(Bytes, offset);
    offet += 4;
    return reinterpret_cast<float&>(ret);
}

String readOSCString(const TIdBytes &Bytes, int &offset)
{
    int found = ByteIndex(0x00, Bytes, offset);
    if (found == -1) throw ...; // error!
    int len = found - offset;
    String ret = BytesToString(Bytes, offset, len, IndyTextEncoding_ASCII());
    len = ((len + 3) & ~3)); // round up to even multiple of 32 bits
    offset += len;
    return ret;
}

TIdBytes readOSCBlob(const TIdBytes &Bytes, int &offset)
{
    int32_t size = readInt32(Bytes, offset);
    TIdBytes ret;
    ret.Length = size;
    CopyTIdBytes(Bytes, offset, ret, 0, size);
    size = ((size + 3) & ~3)); // round up to even multiple of 32 bits
    offset += size;
    return ret;
}

OSCMessage* readOSCMessage(const TIdBytes &Bytes, int &offset);
OSCBundle* readOSCBundle(const TIdBytes &Bytes, int &offset);

OSCBundleElement* readOSCBundleElement(const TIdBytes &Bytes, int &offset)
{
    TIdBytes data = readOSCBlob(Bytes, offset);
    int dataOffset = 0;

    switch (data[0])
    {
        case '/': return readOSCMessage(data, dataOffset); 
        case '#': return readOSCBundle(data, dataOffset);
    }

    throw ...; // unknown data!
}

Variant readOSCArgumentData(char ArgType, const TIdBytes &Bytes, int &offset)
{
    switch (ArgType)
    {
        case 'i': return readInt32(Bytes, offset);
        case 'f': return readFloat32(Bytes, offset);
        case 's': return readOSCString(Bytes, offset);
        case 'b': return readOSCBlob(Bytes, offset);
        // other types as needed ...
    }

    throw ...; // unknown data!
}

OSCMessage* readOSCMessage(const TIdBytes &Bytes, int &offset)
{
    OSCMessage* ret = new OSCMessage;
    try
    {
        ret->AddressPattern = readOSCString(Bytes, offset);

        String ArgumentTypes = readOSCString(Bytes, offset);
        if (ArgumentTypes[1] != ',') throw ...; // error!

        for (int i = 2; i <= ret->ArgumentTypes.Length(); ++i)
        {
            OSCArgument arg;
            arg.Type = ArgumentTypes[i];
            arg.Data = readOSCArgumentData(arg.Type, Bytes, offset);

            ret.Arguments.Length = ret.Arguments.Length + 1;
            ret.Arguments[ret.Arguments.High] = arg;
        }
    }
    catch (...)
    {
        delete ret;
        throw;
    }

    return ret;
}

OSCBundle* readOSCBundle(const TIdBytes &Bytes, int &offset)
{
    if (readOSCString(Bytes, offset) != "#bundle")
        throw ...; // error!

    OSCBundle *ret = new OSCBundle;
    try
    {
        ret->TimeTag = readOSCTimeTag(Bytes, offset);

        int len = Bytes.Length;
        while (offset < len)
        {
            OSCBundleElement *element = readOSCBundleElement(Bytes, offset);
            try
            {
                ret->Elements.Length = ret->Elements.Length + 1;
                ret->Elements[ret->Elements.High] = element;
            }
            catch (...)
            {
                delete element;
                throw;
            }
        }
    }
    catch (...)
    {
        delete ret;
        throw;
    }

    return ret;
}

void __fastcall TMain::UDPServerUDPRead(TIdUDPListenerThread *AThread, const TIdBytes AData,
    TIdSocketHandle *ABinding)
{
    int offset = 0;

    if (AData[0] == '/')
    {
        OSCMessage *msg = readOSCMessage(AData, offset);
        // process msg as needed...
        delete msg;
    }
    else if (AData[0] == '#')
    {
        OSCBundle *bundle = readOSCBundle(AData, offset); 
        // process bundle as needed...
        delete bundle;
    }
    else
    {
        // unknown data!
    }
}

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