将 'Vector t' 反序列化为 Telegram 客户端中返回的 RpcResult 对象

Deserializing 'Vector t' as a returned RpcResult object in Telegram client

当调用 contacts.getStatus 的电报客户端方法时,return 值的类型为 RpcResult

一个RpcResult定义为:

rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult;

这意味着有一个消息ID和一个对象。然后必须根据从对象的前 4 个字节中提取的数据类型反序列化对象。很好,它适用于目前所有 returned RpcResult 个对象,除了一个:contact.getStatuses.

returned 数据看起来应该是这样,除了 Object 类型是 Vector t

-->('get_server_answer: method: ', 'contacts.getStatuses')
<--('TL tl_elem.type: ', u'RpcResult')

<--('TL deserialize: type     =  ', u'long')
<--('TL deserialize: subtype  = ', None)

<--('TL deserialize: type     =  ', u'Object')
<--('TL tl_elem.type: ', u'Vector t')

由于向量是 t 类型项的集合,我们需要知道 t 代表什么类型,但 returned 数据中没有任何内容表明这一点。

由于 contacts.getStatuses 的已发布 return 类型是 Vector<ContactStatus> 类型,我可以将 t 的类型硬编码为 ContactStatus 但仅因为我在打电话之前就知道 t 的类型。恕我直言,这将是一个糟糕的解决方案,因为它在反序列化传入数据时假定先验知识。

当 return 作为 RpcResult 中的对象时,我如何知道 Vector t 的类型?

顺便说一句:如果我反序列化传入数据的下 4 个字节,则该值为零。这可能意味着向量中有零个项目。但这仍然不能帮助我确定 t 应该是什么数据类型 向量中的项目。

这是从字节流的角度来看的另一种方式:

来自电报服务器的

字节 return:

0  | 01 6D 5C F3 00 E4 D0 22
8  | AF 90 34 58 15 C4 B5 1C
16 | 00 00 00 00

由于前四个字节是 F3 5C 6D 01(小端顺序),表示 RpcResult 数据类型已被 returned:

rpc_result#f35c6d01 req_msg_id:long result:Object = RpcResult;

这意味着我现在期待 long(req_msg_id)和 Object(结果)。

所以接下来的 8 个字节(长)应该是 req_msg_id:00 E4 D0 22 AF 90 34 58

接下来的四个字节表示returned Object类型:1C B5 C4 15 ...这是一个 Vector:

vector#1cb5c415 {t:Type} # [ t ] = Vector t;

只留下最后四个字节,它们是零:00 00 00 00

这是否意味着向量中有零个项目?

如果这不是零,我将如何确定(从字节流中)向量中的数据类型是什么?

这是完整的字节流,从上一个成功的方法调用 (user.getFullUser) 开始,然后是我在反序列化时遇到问题的方法调用结果 (contacts.getStatuses ) 因为未识别 Vector 中的类型:

('method_call: ', 'users.getFullUser', {'id': {'inputPeerSelf': {}}})
--> send_message: sending...

<-- Result:
('TL tl_elem.type: ', u'RpcResult')
('TL deserialize: type     =  ', u'long')
('TL deserialize: type     =  ', u'Object')
 0 | 01 6D 5C F3 00 F4 FD 94  <-- RpcResult (F3 5C 6D 01)
 8 | F6 F0 35 58 03 FC 32 59
16 | 00 00 00 00 9A 97 0D D1
24 | 57 04 00 00 61 1F 02 00
32 | 60 BA 2D 1C 69 6E 9F C4
40 | 05 4B 65 6E 6E 79 00 00
48 | 05 43 61 73 6F 6E 00 00
56 | 0B 31 32 30 36 35 35 30
64 | 30 30 34 39 49 39 B9 ED
72 | 06 F2 35 58 4C 48 CE 3A
80 | AD D3 ED FE 47 92 4F 5F
88 | 9A 97 0D D1 57 04 00 00
96 | 61 1F 02 00 60 BA 2D 1C
104 | 69 6E 9F C4 05 4B 65 6E
112 | 6E 79 00 00 05 43 61 73
120 | 6F 6E 00 00 0B 31 32 30
128 | 36 35 35 30 30 30 34 39
136 | 49 39 B9 ED 06 F2 35 58
144 | C0 A4 CD 9A 01 00 00 00
152 | 00 00 00 00 07 64 65 66
160 | 61 75 6C 74

('TL tl_elem.type: ', u'UserFull')
('TL deserialize: type     =  ', u'#')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', u'User')

('TL tl_elem.type: ', u'User')
('TL deserialize: type     =  ', u'#')
('TL deserialize: type     =  ', u'true')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', u'int')
('TL deserialize: type     =  ', u'long')
('TL deserialize: type     =  ', u'string')
('TL deserialize: type     =  ', u'string')
('TL deserialize: type     =  ', u'string')
('TL deserialize: type     =  ', u'UserStatus')

('TL tl_elem.type: ', u'UserStatus')
('TL deserialize: type     =  ', u'int')

('TL tl_elem.type: ', u'contacts.Link')
('TL deserialize: type     =  ', u'ContactLink')

('TL tl_elem.type: ', u'ContactLink')
('TL deserialize: type     =  ', u'ContactLink')

('TL tl_elem.type: ', u'ContactLink')
('TL deserialize: type     =  ', u'User')

('TL tl_elem.type: ', u'User')
('TL deserialize: type     =  ', u'#')
('TL deserialize: type     =  ', u'true')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', u'int')
('TL deserialize: type     =  ', u'long')
('TL deserialize: type     =  ', u'string')
('TL deserialize: type     =  ', u'string')
('TL deserialize: type     =  ', u'string')
('TL deserialize: type     =  ', u'UserStatus')

('TL tl_elem.type: ', u'UserStatus')
('TL deserialize: type     =  ', u'int')

('TL deserialize: type     =  ', u'PeerNotifySettings')
('TL tl_elem.type: ', u'PeerNotifySettings')
('TL deserialize: type     =  ', u'#')
('TL deserialize: type     =  ', u'true')
('TL deserialize: type     =  ', 'false')
('TL deserialize: type     =  ', u'int')
('TL deserialize: type     =  ', u'string')

-----------------------------------------------------
('user.getFullUser: ', 
{u'req_msg_id': 6356251390955615232L,
u'result': {u'link': 
{u'user': {u'last_name': 'Cason', u'status': {u'expires': 1479930374}, u'first_name': 'Kenny', u'bot_nochats': False, u'verified': False, 

u'bot_chat_history': False, u'min': False, u'deleted': False, u'restricted': False, u'self': True, u'bot': False, u'phone': '12069500049', 

u'contact': False, u'flags': 1111, u'bot_inline_geo': False, u'access_hash': -4278579723255301539L, u'mutual_contact': False, u'id': 139109}, 

u'my_link': {}, u'foreign_link': {}}, u'notify_settings': {u'show_previews': True, u'mute_until': 0, u'sound': 'default', u'flags': 1, u'silent': 

False}, u'flags': 0, u'user': {u'last_name': 'Cason', u'status': {u'expires': 1479930374}, u'first_name': 'Kenny', u'bot_nochats': False, 

u'verified': False, u'bot_chat_history': False, u'min': False, u'deleted': False, u'restricted': False, u'self': True, u'bot': False, u'phone': 

'12069500049', u'contact': False, u'flags': 1111, u'bot_inline_geo': False, u'access_hash': -4278579723255301539L, u'mutual_contact': False, 

u'id': 139109}, u'blocked': False}})
-----------------------------------------------------

('method_call: ', 'contacts.getStatuses', {})
--> send_message: sending...

<-- Result:
('TL tl_elem.type: ', u'RpcResult')
('TL deserialize: type     =  ', u'long')
('TL deserialize: type     =  ', u'Object')
 0 | 01 6D 5C F3 00 2C 87 B6  <-- RpcResult
 8 | F9 F0 35 58 15 C4 B5 1C
16 | 00 00 00 00

此时我原来的问题依然存在:如何判断上面接收到的vector的类型?

这是我处理向量的解码器的一部分(用 Elixir 编写)

您可能会得到一个简单打包的类型数组 end-to-end,在这种情况下,我使用 dec_o 变体,在这里我根据二进制文件的 header 检查预期类型解码后,我一直这样做,直到我从接收到的字节流中删除了 N 个给定类型 - 这是我可以解码类型列表的方式,其中 vectore header 没有给出计数。

第二种方法更简单,您有一个前导类型 header 、一个向量 header 和预期项目的计数。在这种情况下,我调用下面列出的 dec_v 变体

希望这对您有所帮助...

  defp decode([type], <<21, 196, 181, 28, cnt::little-4*8, bin::binary>>), do: dec_v(type, bin, cnt, [])
  defp decode([_typ], <<code::little-4*8, _::binary>> = bin), do: dec_o(code, bin, [])
  defp decode(_, bin), do: decode(bin)
  defp decode(bin, flag, ix) when band(flag, ix) == ix, do: decode(bin)
  defp decode(bin, _, _), do: {nil, bin}
  defp decode(type, bin, flag, ix) when band(flag, ix) == ix, do: decode(type, bin)
  defp decode(_, bin, _, _), do: {nil, bin}
  defp dec_v(_, e, 0, acc), do: {Enum.reverse(acc), e}
  defp dec_v(_,"", _, acc), do: {Enum.reverse(acc),""}
  defp dec_v(type, bin, cnt, acc) do
    {v, e} = decode(type, bin)
    dec_v(type, e, cnt - 1, [v | acc])
  end
  defp dec_o(c0, <<c1::little-4*8, _::binary>> = bin, acc) when c0 != c1, do: {Enum.reverse(acc), bin}
  defp dec_o(c0, <<c1::little-4*8, _::binary>> = bin, acc) when c0 == c1 do
    {v, e}= decode(bin)
    dec_o(c0, e, [v | acc])
  end

更多示例

将此应用于上述问题的实际数据:

Erlang/OTP 19 [erts-8.1] [64-bit] [smp:8:8] [async-threads:10]

Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> x = 0x016D5CF300F4FD94F6F0355803FC3259000000009A970DD157040000611F020060BA2D1C696E9FC4054B656E6E790000054361736F6E00000B31323036353530303034394939B9ED06F235584C48CE3AADD3EDFE47924F5F9A970DD157040000611F020060BA2D1C696E9FC4054B656E6E790000054361736F6E00000B31323036353530303034394939B9ED06F23558C0A4CD9A01000000000000000764656661756C74

iex(3)> TL.hex x
{%TL.Rpc_Result{req_msg_id: 6356251390955615232,
  result: %TL.UserFull{about: nil, blocked: nil, bot_info: nil, flags: 0,
   link: %TL.Contacts.Link{foreign_link: %TL.ContactLinkUnknown{},
    my_link: %TL.ContactLinkNone{},
    user: %TL.User{access_hash: 14168164350454250080, bot: nil,
     bot_chat_history: nil, bot_info_version: nil, bot_inline_geo: nil,
     bot_inline_placeholder: nil, bot_nochats: nil, contact: nil, deleted: nil,
     first_name: "Kenny", flags: 1111, id: 139105, last_name: "Cason", min: nil,
     mutual_contact: nil, phone: "12065500049", photo: nil, restricted: nil,
     restriction_reason: nil, self: true,
     status: %TL.UserStatusOnline{expires: 1479930374}, username: nil,
     verified: nil}},
   notify_settings: %TL.PeerNotifySettings{flags: 1, mute_until: 0,
    show_previews: true, silent: nil, sound: "default"}, profile_photo: nil,
   user: %TL.User{access_hash: 14168164350454250080, bot: nil,
    bot_chat_history: nil, bot_info_version: nil, bot_inline_geo: nil,
    bot_inline_placeholder: nil, bot_nochats: nil, contact: nil, deleted: nil,
    first_name: "Kenny", flags: 1111, id: 139105, last_name: "Cason", min: nil,
    mutual_contact: nil, phone: "12065500049", photo: nil, restricted: nil,
    restriction_reason: nil, self: true,
    status: %TL.UserStatusOnline{expires: 1479930374}, username: nil,
    verified: nil}}}, ""}