【问题标题】:Delphi C++ Indy UDP Server BytesToString does not receive correct string?Delphi C++ Indy UDP Server BytesToString 没有收到正确的字符串?
【发布时间】:2018-11-22 09:36:19
【问题描述】:

我正在尝试从 TUIO 多模拟器接收 UDP 数据包。但是有些数据丢失了。

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" 作为字符串,但是当使用第二个循环时,我得到了这个:

# b 你 n d l e

/ 吨 你 一世 ○ / 2 D C 你 r , s 一世 一种 l 一世 v e 4 / 吨 你 一世 ○ / 2 D C 你 r , s 一世 F F F F F s e 吨 ?

ホ 9 >  ᅳ

/ 吨 你 一世 ○ / 2 D C 你 r , s 一世 F s e q # b 你 n d l e

/ 吨 你 一世 ○ / 2 D ○ b j , s 一种 l 一世 v e

/ 吨 你 一世 ○ / 2 D ○ b j , s 一世 F s e q # b 你 n d l e

/ 吨 你 一世 ○ / 2 D C 你 r , s 一种 l 一世 v e

/ 吨 你 一世 ○ / 2 D C 你 r , s 一世 F s e q

当然,每个字符单独一行。

那么,当我不知道编码时,如何获取正确的数据呢?我在这里迷路了,需要真正的帮助。

另外,如何使用TIdUDPClient 发送类似的捆绑数据?

【问题讨论】:

  • 那么,发送的数据是什么编码的呢?为什么你一次处理一个字节?你怎么知道整个包裹已经到了?
  • @DavidHeffernan "你怎么知道整个包已经到达?" - UDP 是面向消息的,而不是像 TCP 那样面向流的。 TIdUDPServer 只接收完整消息,不接收部分消息。

标签: delphi udp c++builder indy


【解决方案1】:

您显示的 UDP 数据主要是二进制数据,其中包含一些文本元素。因此,您不应该将整个 AData 转换为带有BytesToString() 的单个字符串,因为它不是完全以文本开头的(好吧,您可以使用BytesToStringRaw(),或者BytesToString()IndyTextEncoding_8bit() 作为编码,但这样做对你没有多大帮助)。

您需要根据您链接到的protocol specificiation(又基于Open Sound Control 协议)将数据分解为其各个组件。只有这样,您才能根据需要处理组件。这意味着您需要逐个字节地遍历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!
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-21
    • 2017-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-04
    • 1970-01-01
    相关资源
    最近更新 更多