krpc规范


KRPC简介

KRPC 协议是一种简单的 RPC 机制,由通过 UDP 发送的编码字典组成。发送单个查询数据包并发送单个数据包作为响应。没有重试。共有三种消息类型:查询、响应和错误。对于 DHT 协议,有四种查询:ping、find_node、get_peers 和 announce_peer

KRPC 消息是一个字典,每个消息都有三个通用的键,并且根据消息的类型还有其他键。

每条消息都有一个键“t”和一个代表交易 ID 的字符串值。该事务 ID 由查询节点生成并在响应中回显,因此响应可能与对同一节点的多个查询相关联。交易 ID 应编码为二进制数字的短字符串,通常 2 个字符就足够了,因为它们涵盖 2^16 个未完成的查询。每条消息还有一个键“y”,带有描述消息类型的单个字符值。“y”键的值是查询的“q”、响应的“r”或错误的“e”之一。每个带有客户端版本字符串的消息中都应包含一个键“v”。后跟两个字符的版本标识符。并非所有实现都包含“v”键,因此客户端不应假定它存在。

{
    "t" : "ID", //交易ID——string 编码为二进制数字的短字符串
    "y" : "q", //消息类型 "q"查询/"r"响应/"e"错误 
    "v" : "", //版本标识符(两个字符)
}

接触编码(Contact Encoding)

peers之间的交互信息被编码为一个 6 字节的字符串。也称为“协议 IP 地址/端口信息”(Compact IP-address/port info),

4 字节 IP 地址按网络字节顺序排列,2 字节端口按网络字节顺序排列在末尾。

节点的交互信息被编码为 26 字节的字符串。也称为“协议节点信息”(Compact node info),网络字节顺序的 20 字节节点 ID 具有连接到末尾的协议 IP 地址/端口信息。

查询(Queries)

“y”值为“q”的查询或 KRPC 消息字典包含两个附加键;“q”和“a”。键“q”有一个包含查询方法名称的字符串值。键“a”有一个字典值,其中包含查询的命名参数。

{
    "q": "", //查询方法名称——字符串
    "a": "", //查询命名参数
}

响应(Responses)

“y”值为“r”的响应或 KRPC 消息字典包含一个附加键“r”。“r”的值是一个包含命名返回值的字典。成功完成查询后发送响应消息。

{
    "e":"", //返回值
}

错误(Errors)

“y”值为“e”的错误或 KRPC 消息字典包含一个附加键“e”。“e”的值是一个列表。第一个元素是一个表示错误代码的整数。第二个元素是包含错误消息的字符串。当查询无法完成时发送错误。下表描述了可能的错误代码:

Code Description
201 一般错误
202 服务器错误
203 协议错误,例如格式错误的数据包、无效参数或错误令牌
204 未知的方法
{
    "e":[
        code, //错误代码
        message, //错误信息
    ]
}

DHT查询

所有查询都有一个“id”键和值,其中包含查询节点的节点 ID。所有响应都有一个“id”键和值,其中包含响应节点的节点 ID。

ping

最基本的查询是 ping。”q” = “ping” ping 查询有一个参数,”id” 值是一个 20 字节的字符串,包含按网络字节顺序排列的发送方节点 ID。对 ping 的适当响应具有单个键“id”,其中包含响应节点的节点 ID。

arguments:  {"id" : "<querying nodes id>"}

response: {"id" : "<queried nodes id>"}

示例数据包

ping Query = {"t":"aa", "y":"q", "q":"ping", "a":{"id":"abcdefghij0123456789"}}
bencoded = d1:ad2:id20:abcdefghij0123456789e1:q4:ping1:t2:aa1:y1:qe
Response = {"t":"aa", "y":"r", "r": {"id":"mnopqrstuvwxyz123456"}}
bencoded = d1:rd2:id20:mnopqrstuvwxyz123456e1:t2:aa1:y1:re

find_node

查找节点用于查找给定 ID 的节点的联系信息。”q” == “find_node” find_node 查询有两个参数,”id” 包含查询节点的节点 ID,”target” 包含查询者查找的节点的 ID。当一个节点收到一个 find_node 查询时,它应该用一个键“nodes”和一个字符串的值来响应,该字符串包含目标节点或它自己的路由表中 K (8) 个最接近的好节点的紧凑节点信息。

arguments:  {"id" : "<querying nodes id>", "target" : "<id of target node>"}

response: {"id" : "<queried nodes id>", "nodes" : "<compact node info>"}

示例数据包

find_node Query = {"t":"aa", "y":"q", "q":"find_node", "a": {"id":"abcdefghij0123456789", "target":"mnopqrstuvwxyz123456"}}
bencoded = d1:ad2:id20:abcdefghij01234567896:target20:mnopqrstuvwxyz123456e1:q9:find_node1:t2:aa1:y1:qe
Response = {"t":"aa", "y":"r", "r": {"id":"0123456789abcdefghij", "nodes": "def456..."}}
bencoded = d1:rd2:id20:0123456789abcdefghij5:nodes9:def456...e1:t2:aa1:y1:re

get_peers

获取与 torrent infohash 关联的peer节点。”q” = “get_peers” get_peers 查询有两个参数,”id” 包含查询节点的节点 ID,”info_hash” 包含 torrent 的 infohash。如果查询的节点有 infohash 的对等节点,它们将作为字符串列表在键“值”中返回。每个字符串包含单个对等点的“紧凑”格式对等点信息。如果查询的节点没有 infohash 的对等节点,则返回一个键“nodes”,其中包含查询节点路由表中最接近查询中提供的 infohash 的 K 个节点。在任何一种情况下,“令牌”键也包含在返回值中。令牌值是未来 announce_peer 查询的必需参数。令牌值应该是一个短的二进制字符串。

arguments:  {"id" : "<querying nodes id>", "info_hash" : "<20-byte infohash of target torrent>"}

response: {"id" : "<queried nodes id>", "token" :"<opaque write token>", "values" : ["<peer 1 info string>", "<peer 2 info string>"]}

or: {"id" : "<queried nodes id>", "token" :"<opaque write token>", "nodes" : "<compact node info>"}

示例数据包

get_peers Query = {"t":"aa", "y":"q", "q":"get_peers", "a": {"id":"abcdefghij0123456789", "info_hash":"mnopqrstuvwxyz123456"}}
bencoded = d1:ad2:id20:abcdefghij01234567899:info_hash20:mnopqrstuvwxyz123456e1:q9:get_peers1:t2:aa1:y1:qe
Response with peers = {"t":"aa", "y":"r", "r": {"id":"abcdefghij0123456789", "token":"aoeusnth", "values": ["axje.u", "idhtnm"]}}
bencoded = d1:rd2:id20:abcdefghij01234567895:token8:aoeusnth6:valuesl6:axje.u6:idhtnmee1:t2:aa1:y1:re
Response with closest nodes = {"t":"aa", "y":"r", "r": {"id":"abcdefghij0123456789", "token":"aoeusnth", "nodes": "def456..."}}
bencoded = d1:rd2:id20:abcdefghij01234567895:nodes9:def456...5:token8:aoeusnthe1:t2:aa1:y1:re

announce_peer

announce_peer 有四个参数:“id”包含查询节点的节点 ID,“info_hash”包含 torrent 的 infohash,“port”是整数类型的端口,以及响应先前 get_peers 查询收到的“token” . 被查询节点必须验证令牌之前是否已发送到与查询节点相同的 IP 地址。然后,被查询节点应将查询节点的 IP 地址和提供的端口号存储在其对等联系信息存储中的 infohash 下。

有一个名为implied_port的可选参数,其值为 0 或 1。如果它存在且非零,则端口参数应被忽略,UDP 数据包的源端口应用作对等端口。这对于可能不知道其外部端口的 NAT 后面的对等点很有用,并且支持 UTP,它们在与 DHT 端口相同的端口上接受传入连接。

arguments:  {"id" : "<querying nodes id>",
  "implied_port": <0 or 1>,
  "info_hash" : "<20-byte infohash of target torrent>",
  "port" : <port number>,
  "token" : "<opaque token>"}

response: {"id" : "<queried nodes id>"}

示例数据包

announce_peers Query = {"t":"aa", "y":"q", "q":"announce_peer", "a": {"id":"abcdefghij0123456789", "implied_port": 1, "info_hash":"mnopqrstuvwxyz123456", "port": 6881, "token": "aoeusnth"}}
bencoded = d1:ad2:id20:abcdefghij012345678912:implied_porti1e9:info_hash20:mnopqrstuvwxyz1234564:porti6881e5:token8:aoeusnthe1:q13:announce_peer1:t2:aa1:y1:qe
Response = {"t":"aa", "y":"r", "r": {"id":"mnopqrstuvwxyz123456"}}
bencoded = d1:rd2:id20:mnopqrstuvwxyz123456e1:t2:aa1:y1:re

文章作者: wck
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 wck !
评论
  目录