libtorrent源码分析(四)LSD实现

1. 前言

  本文分析libtorrent中LSD功能的实现源码。LSD,即Local Service Discovery,是BT下载中局域网内客户端寻找、发现节点的方法,简洁有效,易于实现。其基本原理是采用组播的形式发送报文给指定的IP网段,然后接收、解析并判断。

2. LSD协议基本内容

(1)网段
  包括IPV4和IPV6:A) 239.192.152.143:6771 (org-local) and B) [ff15::efc0:988f]:6771

(2)announce内容

BT-SEARCH * HTTP/1.1\r\n
Host: <host>\r\n
Port: <port>\r\n
Infohash: <ihash>\r\n
cookie: <cookie (optional)>\r\n
\r\n
\r\n

其中host为(1)中地址,port为自己的端口, infohash提取自torrent/magnet文件,cookie为可选项,用于过滤自己产生的回环消息使用。

3. 源码分析

(1)报文消息的生成

  这里其实很简单,就是一个字符串的替换而已

int render_lsd_packet(char* dst, int const len, int const listen_port
	, char const* info_hash_hex, int const cookie, char const* host)
{
	TORRENT_ASSERT(len > 0);
	return std::snprintf(dst, aux::numeric_cast<std::size_t>(len),
		"BT-SEARCH * HTTP/1.1\r\n"
		"Host: %s:6771\r\n"
		"Port: %d\r\n"
		"Infohash: %s\r\n"
		"cookie: %x\r\n"
		"\r\n\r\n", host, listen_port, info_hash_hex, cookie);
}

(2)报文解析

void lsd::on_announce(udp::endpoint const& from, span<char const> buf)
{
	//这里是一个http消息解释器,其实就是分类读取announce报文中的各段消息,可以轻松实现
	http_parser p;

	bool error = false;
	p.incoming(buf, error);

	//检测http消息格式,是否有HTTP/1.1\r\n
	if (!p.header_finished() || error)
	{
#ifndef TORRENT_DISABLE_LOGGING
		debug_log("<== LSD: incomplete HTTP message");
#endif
		return;
	}

	//检测是否是BT查询
	if (p.method() != "bt-search")
	{
#ifndef TORRENT_DISABLE_LOGGING
		debug_log("<== LSD: invalid HTTP method: %s", p.method().c_str());
#endif
		return;
	}

	//检测并记录端口
	std::string const& port_str = p.header("port");
	if (port_str.empty())
	{
#ifndef TORRENT_DISABLE_LOGGING
		debug_log("<== LSD: invalid BT-SEARCH, missing port");
#endif
		return;
	}

	long const port = std::strtol(port_str.c_str(), nullptr, 10);
	if (port <= 0 || port >= int(std::numeric_limits<std::uint16_t>::max()))
	{
#ifndef TORRENT_DISABLE_LOGGING
		debug_log("<== LSD: invalid BT-SEARCH port value: %s", port_str.c_str());
#endif
		return;
	}

	auto const& headers = p.headers();

	//如果有cookie则根据cookie进行判断是否是本地loopback
	auto const cookie_iter = headers.find("cookie");
	if (cookie_iter != headers.end())
	{
		// we expect it to be hexadecimal
		// if it isn't, it's not our cookie anyway
		long const cookie = std::strtol(cookie_iter->second.c_str(), nullptr, 16);
		if (cookie == m_cookie)
		{
#ifndef TORRENT_DISABLE_LOGGING
			debug_log("<== LSD: ignoring packet (cookie matched our own): %x"
				, m_cookie);
#endif
			return;
		}
	}

	//infohash的判断:相同的infohash则表示下载同一个文件,会加入到节点之中进行后续处理
	auto const ihs = headers.equal_range("infohash");
	for (auto i = ihs.first; i != ihs.second; ++i)
	{
		std::string const& ih_str = i->second;
		if (ih_str.size() != 40)
		{
#ifndef TORRENT_DISABLE_LOGGING
			debug_log("<== LSD: invalid BT-SEARCH, invalid infohash: %s"
				, ih_str.c_str());
#endif
			continue;
		}

		sha1_hash ih;
		aux::from_hex(ih_str, ih.data());

		//判断成功,则加入节点中,进行后续peer wire协议
		if (!ih.is_all_zeros())
		{
#ifndef TORRENT_DISABLE_LOGGING
			if (should_log())
			{
				debug_log("<== LSD: %s:%d ih: %s"
					, print_address(from.address()).c_str()
					, int(port), ih_str.c_str());
			}
#endif
			// we got an announce, pass it on through the callback
			m_callback.on_lsd_peer(tcp::endpoint(from.address(), std::uint16_t(port)), ih);
		}
	}
}

(3)除此之外就是发送和接收部分,调用boost库进行处理,实现非常容易,代码也清晰易懂,就不做赘述了

发布了129 篇原创文章 · 获赞 15 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u013354486/article/details/103914422