Delphi C++ Indy UDP 服务器 BytesToString 没有收到正确的字符串?
Delphi C++ Indy UDP Server BytesToString does not receive correct string?
我正在尝试从 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
升
e
/
吨
你
一世
o
/
2个
丁
C
你
r
,
秒
一世
一种
升
一世
v
电子
4个
/
吨
你
一世
o
/
2个
丁
C
你
r
,
秒
一世
F
F
F
F
F
秒
电子
吨
?
ホ
9
>
ᅳ
/
吨
你
一世
o
/
2个
丁
C
你
r
,
秒
一世
F
秒
电子
q
#
b
你
n
d
升
e
/
吨
你
一世
o
/
2个
丁
o
b
j
,
秒
一种
升
一世
v
e
/
吨
你
一世
o
/
2个
丁
o
b
j
,
秒
一世
F
秒
电子
q
#
b
你
n
d
升
e
/
吨
你
一世
o
/
2个
丁
C
你
r
,
秒
一种
升
一世
v
e
/
吨
你
一世
o
/
2个
丁
C
你
r
,
秒
一世
F
秒
电子
q
当然,每个字符单独一行。
那么,在不知道编码的情况下,如何获取正确的数据呢?我在这里迷路了,真的需要帮助。
此外,我如何发送与 TIdUDPClient
类似的捆绑包数据?
您显示的 UDP 数据主要是二进制数据,其中包含一些文本元素。因此,您不应该将 entire AData
转换为带有 BytesToString()
的单个字符串,因为它不完全是文本开头(好吧,您可以,使用 BytesToStringRaw()
或 BytesToString()
和 IndyTextEncoding_8bit()
作为编码,但这样做对你帮助不大。
您需要根据 protocol specificiation that you linked to (which in turn is based on the 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!
}
}
我正在尝试从 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 升 e
/ 吨 你 一世 o / 2个 丁 C 你 r , 秒 一世 一种 升 一世 v 电子 4个 / 吨 你 一世 o / 2个 丁 C 你 r , 秒 一世 F F F F F 秒 电子 吨 ?
ホ 9 > ᅳ
/ 吨 你 一世 o / 2个 丁 C 你 r , 秒 一世 F 秒 电子 q # b 你 n d 升 e
/ 吨 你 一世 o / 2个 丁 o b j , 秒 一种 升 一世 v e
/ 吨 你 一世 o / 2个 丁 o b j , 秒 一世 F 秒 电子 q # b 你 n d 升 e
/ 吨 你 一世 o / 2个 丁 C 你 r , 秒 一种 升 一世 v e
/ 吨 你 一世 o / 2个 丁 C 你 r , 秒 一世 F 秒 电子 q
当然,每个字符单独一行。
那么,在不知道编码的情况下,如何获取正确的数据呢?我在这里迷路了,真的需要帮助。
此外,我如何发送与 TIdUDPClient
类似的捆绑包数据?
您显示的 UDP 数据主要是二进制数据,其中包含一些文本元素。因此,您不应该将 entire AData
转换为带有 BytesToString()
的单个字符串,因为它不完全是文本开头(好吧,您可以,使用 BytesToStringRaw()
或 BytesToString()
和 IndyTextEncoding_8bit()
作为编码,但这样做对你帮助不大。
您需要根据 protocol specificiation that you linked to (which in turn is based on the 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!
}
}