区块链自动组网逻辑

好的,让我们更详细地解读比特币网络中的组网逻辑。组网逻辑主要包括以下几个方面:DNS种子解析、节点连接管理和连接建立与管理。

1. DNS种子解析

DNS种子解析是自动组网的第一步。比特币客户端使用DNS种子服务器来获取初始节点列表。这些种子服务器会返回一组节点的IP地址,客户端可以尝试连接这些节点。

void CConnman::ThreadDNSAddressSeed()
{
    // 如果地址管理器中已经有地址,并且没有强制使用DNS种子,则跳过DNS种子解析
    if ((addrman.size() > 0) && (!gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED))) {
        if (!interruptNet.sleep_for(std::chrono::seconds(11)))
            return;

        LOCK(cs_vNodes);
        int nRelevant = 0;
        for (auto pnode : vNodes) {
            nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound;
        }
        if (nRelevant >= 2) {
            LogPrintf("P2P peers available. Skipped DNS seeding./n");
            return;
        }
    }

    const std::vector &vSeeds = Params().DNSSeeds();
    int found = 0;

    LogPrintf("Loading addresses from DNS seeds (could take a while)/n");

    for (const std::string &seed : vSeeds) {
        if (interruptNet) {
            return;
        }
        if (HaveNameProxy()) {
            AddOneShot(seed);
        } else {
            std::vector vIPs;
            std::vector vAdd;
            ServiceFlags requiredServiceBits = GetDesirableServiceFlags(NODE_NONE);
            std::string host = strprintf("%s", seed);
            CNetAddr resolveSource;
            if (!resolveSource.SetInternal(host)) {
                continue;
            }
            unsigned int nMaxIPs = 256; // 限制从DNS种子获取的IP数量
            if (LookupHost(host.c_str(), vIPs, nMaxIPs, true))
            {
                for (const CNetAddr& ip : vIPs)
                {
                    int nOneDay = 24*3600;
                    CAddress addr = CAddress(CService(ip, Params().GetDefaultPort()), requiredServiceBits);
                    addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // 使用随机的上次见到时间
                    vAdd.push_back(addr);
                    found++;
                }
                addrman.Add(vAdd, resolveSource);
            } else {
                AddOneShot(seed);
            }
        }
    }

    LogPrintf("%d addresses found from DNS seeds/n", found);
}

2. 节点连接管理

节点连接管理负责维护与其他节点的连接。它会定期尝试连接新的节点,并管理现有的连接。

void CConnman::ThreadOpenConnections(const std::vector connect)
{
    // 如果指定了连接地址,则连接这些地址
    if (!connect.empty())
    {
        for (int64_t nLoop = 0;; nLoop++)
        {
            ProcessOneShot();
            for (const std::string& strAddr : connect)
            {
                CAddress addr(CService(), NODE_NONE);
                OpenNetworkConnection(addr, false, nullptr, strAddr.c_str(), false, false, true);
                for (int i = 0; i < 10 && i < nLoop; i++)
                {
                    if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
                        return;
                }
            }
            if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
                return;
        }
    }

    // 启动网络连接
    int64_t nStart = GetTime();

    // 下次feeler连接的最小时间(以微秒为单位)。
    int64_t nNextFeeler = PoissonNextSend(nStart*1000*1000, FEELER_INTERVAL);
    while (!interruptNet)
    {
        ProcessOneShot();

        if (!interruptNet.sleep_for(std::chrono::milliseconds(500)))
            return;

        CSemaphoreGrant grant(*semOutbound);
        if (interruptNet)
            return;

        // 如果DNS种子全部失效,则添加固定种子节点。
        if (addrman.size() == 0 && (GetTime() - nStart > 1)) {
            static bool done = false;
            if (!done) {
                LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available./n");
                CNetAddr local;
                local.SetInternal("fixedseeds");
                addrman.Add(convertSeed6(Params().FixedSeeds()), local);
                done = true;
            }
        }

        // 选择一个地址进行连接
        CAddress addrConnect;

        // 每个网络组(/16 for IPv4)只连接一个节点。
        int nOutbound = 0;
        std::set > setConnected;
        {
            LOCK(cs_vNodes);
            for (CNode* pnode : vNodes) {
                if (!pnode->fInbound && !pnode->m_manual_connection) {
                    setConnected.insert(pnode->addr.GetGroup());
                    nOutbound++;
                }
            }
        }

        // Feeler连接
        //
        // 设计目标:
        //  * 增加tried表中的可连接地址数量。
        //
        // 方法:
        //  * 从new表中随机选择一个地址并尝试连接,如果连接成功,则将其添加到tried表中。
        //  * 在节点完成所有外部连接后开始尝试feeler连接。
        //  * 每隔几分钟进行一次feeler连接。
        //
        bool fFeeler = false;

        if (nOutbound >= nMaxOutbound && !GetTryNewOutboundPeer()) {
            int64_t nTime = GetTimeMicros(); // 当前时间(以微秒为单位)。
            if (nTime > nNextFeeler) {
                nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
                fFeeler = true;
            } else {
                continue;
            }
        }

        addrman.ResolveCollisions();

        int64_t nANow = GetAdjustedTime();
        int nTries = 0;
        while (!interruptNet)
        {
            CAddrInfo addr = addrman.SelectTriedCollision();

            // 如果SelectTriedCollision返回无效地址,则选择一个新地址。
            if (!fFeeler || !addr.IsValid()) {
                addr = addrman.Select(fFeeler);
            }

            // 如果选择了无效地址,则重新开始
            if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
                break;

            // 如果尝试了100个地址但没有找到合适的,则停止循环
            nTries++;
            if (nTries > 100)
                break;

            if (IsLimited(addr))
                continue;

            // 只有在30次失败尝试后才考虑最近尝试过的节点
            if (nANow - addr.nLastTry < 600 && nTries < 30)
                continue;

            // 对于非feeler连接,要求所有需要的服务标志,
            // 对于feeler连接,只要求它们是完整节点(因为大多数SPV客户端没有良好的地址数据库可用)
            if (!fFeeler && !HasAllDesirableServiceFlags(addr.nServices)) {
                continue;
            } else if (fFeeler && !MayHaveUsefulAddressDB(addr.nServices)) {
                continue;
            }

            // 不允许非默认端口,除非已经选择了50个无效地址
            if (addr.GetPort() != Params().GetDefaultPort() && nTries < 50)
                continue;

            addrConnect = addr;
            break;
        }

        if (addrConnect.IsValid()) {

            if (fFeeler) {
                // 在连接之前添加少量随机噪声以避免同步。
                int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000);
                if (!interruptNet.sleep_for(std::chrono::milliseconds(randsleep)))
                    return;
                LogPrint(BCLog::NET, "Making feeler connection to %s/n", addrConnect.ToString());
            }

            OpenNetworkConnection(addrConnect, (int)setConnected.size() >= std::min(nMaxConnections - 1, 2), &grant, nullptr, false, fFeeler);
        }
    }
}

3. 连接建立与管理

连接建立与管理负责实际的网络连接尝试。它会检查是否可以连接到指定地址或目标,并创建一个新的节点对象。

CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection)
{
    if (pszDest == nullptr) {
        if (IsLocal(addrConnect))
            return nullptr;

        CNode* pnode = FindNode(static_cast(addrConnect));
        if (pnode)
        {
            LogPrintf("Failed to open new connection, already connected/n");
            return nullptr;
        }
    }

    LogPrint(BCLog::NET, "trying connection %s lastseen=%.1fhrs/n",
        pszDest ? pszDest : addrConnect.ToString(),
        pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);

    const int default_port = Params().GetDefaultPort();
    if (pszDest) {
        std::vector resolved;
        if (Lookup(pszDest, resolved,  default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) {
            addrConnect = CAddress(resolved[GetRand(resolved.size())], NODE_NONE);
            if (!addrConnect.IsValid()) {
                LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s/n", addrConnect.ToString(), pszDest);
                return nullptr;
            }
            LOCK(cs_vNodes);
            CNode* pnode = FindNode(static_cast(addrConnect));
            if (pnode)
            {
                pnode->MaybeSetAddrName(std::string(pszDest));
                LogPrintf("Failed to open new connection, already connected/n");
                return nullptr;
            }
        }
    }

    bool connected = false;
    SOCKET hSocket = INVALID_SOCKET;
    proxyType proxy;
    if (addrConnect.IsValid()) {
        bool proxyConnectionFailed = false;

        if (GetProxy(addrConnect.GetNetwork(), proxy)) {
            hSocket = CreateSocket(proxy.proxy);
            if (hSocket == INVALID_SOCKET) {
                return nullptr;
            }
            connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(), hSocket, nConnectTimeout, &proxyConnectionFailed);
        } else {
            hSocket = CreateSocket(addrConnect);
            if (hSocket == INVALID_SOCKET) {
                return nullptr;
            }
            connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout, manual_connection);
        }
        if (!proxyConnectionFailed) {
            addrman.Attempt(addrConnect, fCountFailure);
        }
    } else if (pszDest && GetNameProxy(proxy)) {
        hSocket = CreateSocket(proxy.proxy);
        if (hSocket == INVALID_SOCKET) {
            return nullptr;
        }
        std::string host;
        int port = default_port;
        SplitHostPort(std::string(pszDest), port, host);
        connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, nullptr);
    }
    if (!connected) {
        CloseSocket(hSocket);
        return nullptr;
    }

    NodeId id = GetNewNodeId();
    uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
    CAddress addr_bind = GetBindAddress(hSocket);
    CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false);
    pnode->AddRef();

    return pnode;
}

4. 小结

比特币网络中的自动组网逻辑通过以下步骤实现:

  1. DNS种子解析:从DNS种子服务器获取初始节点列表,并将这些节点添加到地址管理器中。
  2. 节点连接管理:定期尝试连接新的节点,并管理现有的连接。包括处理feeler连接以发现新的可用节点。
  3. 连接建立与管理:实际尝试与其他节点建立网络连接,并创建新的节点对象。

这些步骤共同确保了比特币网络的连通性和去中心化,使得新节点可以自动加入网络,并与其他节点保持连接。

比特币网络的自动组网技术,虽然最初是为支持比特币的去中心化交易而设计的,但其底层技术和理念具有广泛的应用潜力。以下是一些可能的应用场景:

1. 去中心化文件共享

比特币的P2P网络技术可以用于构建去中心化的文件共享系统,如BitTorrent。通过去中心化的方式,文件可以被分布式存储和共享,避免了单点故障和中心化控制。

2. 去中心化社交网络

去中心化社交网络可以利用类似比特币的节点发现和连接技术,确保用户数据的隐私和安全。用户可以通过P2P网络直接连接和通信,而不需要依赖中心化的服务器。

3. 去中心化身份验证

去中心化身份验证系统可以利用比特币网络的节点发现和连接技术,确保身份验证过程的安全性和可靠性。用户的身份信息可以分布式存储和验证,避免了中心化身份验证系统的安全风险。

4. 物联网(IoT)

在物联网领域,设备可以通过类似比特币的自动组网技术进行互联和通信。这样可以实现设备之间的去中心化管理和数据共享,提高系统的可靠性和安全性。

5. 去中心化云存储

去中心化云存储系统可以利用比特币网络的节点发现和连接技术,确保数据的分布式存储和访问。用户的数据可以被加密并分布式存储在多个节点上,提高数据的安全性和可用性。

6. 去中心化市场和交易平台

去中心化市场和交易平台可以利用比特币网络的自动组网技术,确保买卖双方能够直接连接和交易,而不需要中介机构。这可以降低交易成本,提高交易的透明度和安全性。

7. 去中心化内容分发网络(CDN)

去中心化内容分发网络可以利用比特币的P2P技术,确保内容的高效分发和缓存。通过分布式节点,内容可以更快速地传输到用户,提高用户体验。

8. 灾难恢复和应急通信

在自然灾害或其他紧急情况下,去中心化的P2P网络可以用于建立临时的通信网络。这样的网络不依赖于中心化的基础设施,可以在传统通信网络失效时提供备用的通信手段。

9. 去中心化能源管理

在智能电网和分布式能源系统中,去中心化的P2P网络可以用于能源的分配和管理。通过节点之间的直接通信,可以实现能源的高效分配和利用。

10. 去中心化人工智能和机器学习

去中心化的人工智能和机器学习系统可以利用P2P网络进行数据和模型的分布式训练和共享。这样可以提高计算资源的利用率,并确保数据的隐私和安全。

这些应用场景展示了比特币网络自动组网技术的广泛潜力。通过去中心化的方式,这些系统可以提高安全性、可靠性和效率,避免中心化系统的单点故障和控制风险。

好的,我可以用Python实现一个简单的自动组网示例。这个示例将模拟一个简化的去中心化网络,其中节点可以自动发现并连接到其他节点。

简单的自动组网示例

我们将创建一个节点类,每个节点将尝试连接到其他节点,并维护一个连接的节点列表。为了简化,我们将使用UDP通信。

1. 节点类

import socket
import threading
import time
import random

class Node:
    def __init__(self, port, known_nodes=None):
        self.port = port
        self.known_nodes = known_nodes if known_nodes else []
        self.connected_nodes = []

    def start(self):
        threading.Thread(target=self.listen).start()
        time.sleep(1)  # 等待监听线程启动
        threading.Thread(target=self.discover_nodes).start()

    def listen(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind(('0.0.0.0', self.port))
        print(f'Node listening on port {self.port}')

        while True:
            data, addr = sock.recvfrom(1024)
            message = data.decode()
            if message.startswith('DISCOVER'):
                self.handle_discover(message, addr, sock)
            elif message.startswith('NODE'):
                self.handle_node(message)

    def handle_discover(self, message, addr, sock):
        _, port = message.split()
        port = int(port)
        if port not in self.connected_nodes:
            self.connected_nodes.append(port)
            print(f'Node {self.port} discovered node {port}')
            response = f'NODE {self.port}'
            sock.sendto(response.encode(), addr)

    def handle_node(self, message):
        _, port = message.split()
        port = int(port)
        if port not in self.connected_nodes:
            self.connected_nodes.append(port)
            print(f'Node {self.port} connected to node {port}')

    def discover_nodes(self):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        while True:
            for node_port in self.known_nodes:
                if node_port not in self.connected_nodes:
                    message = f'DISCOVER {self.port}'
                    sock.sendto(message.encode(), ('127.0.0.1', node_port))
            time.sleep(random.randint(1, 5))

# 示例用法
if __name__ == '__main__':
    node1 = Node(5000)
    node2 = Node(5001, known_nodes=[5000])
    node3 = Node(5002, known_nodes=[5000])

    node1.start()
    node2.start()
    node3.start()

2. 解释

  • Node类:每个节点都有一个端口号,并且可以知道其他节点的端口号。
  • start方法:启动节点的监听和发现线程。
  • listen方法:节点监听UDP消息,处理发现请求和节点连接消息。
  • handle_discover方法:处理发现请求并回应自己的端口号。
  • handle_node方法:处理节点连接消息,记录已连接的节点。
  • discover_nodes方法:定期向已知节点发送发现请求。

运行示例

将上述代码保存为一个Python文件(例如auto_network.py),然后运行它。你会看到节点自动发现并连接到其他节点的输出。

python auto_network.py

输出示例:

Node listening on port 5000
Node listening on port 5001
Node 5001 discovered node 5000
Node 5000 connected to node 5001
Node listening on port 5002
Node 5002 discovered node 5000
Node 5000 connected to node 5002
Node 5002 discovered node 5001
Node 5001 connected to node 5002

这个示例展示了一个基本的自动组网过程,节点可以自动发现并连接到其他节点。你可以根据需要扩展这个示例,添加更多功能,如节点断开连接处理、消息传递等。

版权声明:
作者:玉兰
链接:https://www.techfm.club/p/157843.html
来源:TechFM
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>