千家信息网

Bytom的P2P网络地址簿结构体是怎样的

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,这篇文章主要讲解了"Bytom的P2P网络地址簿结构体是怎样的",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Bytom的P2P网络地址簿结构体是怎样的
千家信息网最后更新 2026年10月31日Bytom的P2P网络地址簿结构体是怎样的

这篇文章主要讲解了"Bytom的P2P网络地址簿结构体是怎样的",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Bytom的P2P网络地址簿结构体是怎样的"吧!

addrbook介绍

addrbook用于存储P2P网络中保留最近的对端节点地址 在MacOS下,默认的地址簿路径存储在~/Library/Bytom/addrbook.json

地址簿格式

** ~/Library/Bytom/addrbook.json **

{    "Key": "359be6d08bc0c6e21c84bbb2",    "Addrs": [        {            "Addr": {                "IP": "122.224.11.144",                "Port": 46657            },            "Src": {                "IP": "198.74.61.131",                "Port": 46657            },            "Attempts": 0,            "LastAttempt": "2018-05-04T12:58:23.894057702+08:00",            "LastSuccess": "0001-01-01T00:00:00Z",            "BucketType": 1,            "Buckets": [                181,                10            ]        }    ]}

地址类型

在addrbook中存储的地址有两种: ** p2p/addrbook.go **

const (        bucketTypeNew = 0x01  // 标识新地址,不可靠地址(未成功连接过)。只存储在一个bucket中        bucketTypeOld = 0x02  // 标识旧地址,可靠地址(已成功连接过)。可以存储在多个bucket中,最多为maxNewBucketsPerAddress个)

注意: 一个地址的类型变更不在此文章中做介绍,后期的文章会讨论该问题

地址簿相关结构体

地址簿

type AddrBook struct {        cmn.BaseService        mtx               sync.Mutex        filePath          string  // 地址簿路径        routabilityStrict bool  // 是否可路由,默认为true        rand              *rand.Rand         key               string  // 地址簿标识,用于计算addrNew和addrOld的索引        ourAddrs          map[string]*NetAddress  // 存储本地网络地址,用于添加p2p地址时做排除使用        addrLookup        map[string]*knownAddress // 存储新、旧地址集,用于查询        addrNew           []map[string]*knownAddress // 存储新地址        addrOld           []map[string]*knownAddress // 存储旧地址        wg                sync.WaitGroup        nOld              int // 旧地址数量        nNew              int // 新地址数量}

已知地址

type knownAddress struct {        Addr        *NetAddress // 已知peer的addr        Src         *NetAddress // 已知peer的addr的来源addr        Attempts    int32 // 连接peer的重试次数        LastAttempt time.Time // 最近一次尝试连接的时间        LastSuccess time.Time // 最近一次尝试成功连接的时间        BucketType  byte // 地址的类型(表示可靠地址或不可靠地址)        Buckets     []int // 当前addr所属的buckets}

routabilityStrict参数表示地址簿是否存储的ip是否可路由。可路由是根据RFC划分,具体参考资料:RFC标准

初始化地址簿

// NewAddrBook creates a new address book.// Use Start to begin processing asynchronous address updates.func NewAddrBook(filePath string, routabilityStrict bool) *AddrBook {        am := &AddrBook{                rand:              rand.New(rand.NewSource(time.Now().UnixNano())),                ourAddrs:          make(map[string]*NetAddress),                addrLookup:        make(map[string]*knownAddress),                filePath:          filePath,                routabilityStrict: routabilityStrict,        }        am.init()        am.BaseService = *cmn.NewBaseService(nil, "AddrBook", am)        return am}// When modifying this, don't forget to update loadFromFile()func (a *AddrBook) init() {  // 地址簿唯一标识        a.key = crypto.CRandHex(24) // 24/2 * 8 = 96 bits        // New addr buckets, 默认为256个大小        a.addrNew = make([]map[string]*knownAddress, newBucketCount)        for i := range a.addrNew {                a.addrNew[i] = make(map[string]*knownAddress)        }        // Old addr buckets,默认为64个大小        a.addrOld = make([]map[string]*knownAddress, oldBucketCount)        for i := range a.addrOld {                a.addrOld[i] = make(map[string]*knownAddress)        }}

bytomd启动时加载本地地址簿

loadFromFile在bytomd启动时,首先会加载本地的地址簿

// OnStart implements Service.func (a *AddrBook) OnStart() error {        a.BaseService.OnStart()        a.loadFromFile(a.filePath)        a.wg.Add(1)        go a.saveRoutine()        return nil}// Returns false if file does not exist.// cmn.Panics if file is corrupt.func (a *AddrBook) loadFromFile(filePath string) bool {        // If doesn't exist, do nothing.        // 如果本地地址簿不存在则直接返回        _, err := os.Stat(filePath)        if os.IsNotExist(err) {                return false        }  // 加载地址簿json内容        // Load addrBookJSON{}        r, err := os.Open(filePath)        if err != nil {                cmn.PanicCrisis(cmn.Fmt("Error opening file %s: %v", filePath, err))        }        defer r.Close()        aJSON := &addrBookJSON{}        dec := json.NewDecoder(r)        err = dec.Decode(aJSON)        if err != nil {                cmn.PanicCrisis(cmn.Fmt("Error reading file %s: %v", filePath, err))        }  // 填充addrNew、addrOld等        // Restore all the fields...        // Restore the key        a.key = aJSON.Key        // Restore .addrNew & .addrOld        for _, ka := range aJSON.Addrs {                for _, bucketIndex := range ka.Buckets {                        bucket := a.getBucket(ka.BucketType, bucketIndex)                        bucket[ka.Addr.String()] = ka                }                a.addrLookup[ka.Addr.String()] = ka                if ka.BucketType == bucketTypeNew {                        a.nNew++                } else {                        a.nOld++                }        }        return true}

定时更新地址簿

bytomd会定时更新本地地址簿,默认2分钟一次

func (a *AddrBook) saveRoutine() {        dumpAddressTicker := time.NewTicker(dumpAddressInterval)out:        for {                select {                case <-dumpAddressTicker.C:                        a.saveToFile(a.filePath)                case <-a.Quit:                        break out                }        }        dumpAddressTicker.Stop()        a.saveToFile(a.filePath)        a.wg.Done()        log.Info("Address handler done")}func (a *AddrBook) saveToFile(filePath string) {        log.WithField("size", a.Size()).Info("Saving AddrBook to file")        a.mtx.Lock()        defer a.mtx.Unlock()        // Compile Addrs        addrs := []*knownAddress{}        for _, ka := range a.addrLookup {                addrs = append(addrs, ka)        }        aJSON := &addrBookJSON{                Key:   a.key,                Addrs: addrs,        }        jsonBytes, err := json.MarshalIndent(aJSON, "", "\t")        if err != nil {                log.WithField("err", err).Error("Failed to save AddrBook to file")                return        }        err = cmn.WriteFileAtomic(filePath, jsonBytes, 0644)        if err != nil {                log.WithFields(log.Fields{                        "file": filePath,                        "err":  err,                }).Error("Failed to save AddrBook to file")        }}

添加新地址

当peer之间交换addr时,节点会收到对端节点已知的地址信息,这些信息会被当前节点添加到地址簿中

func (a *AddrBook) AddAddress(addr *NetAddress, src *NetAddress) {        a.mtx.Lock()        defer a.mtx.Unlock()        log.WithFields(log.Fields{                "addr": addr,                "src":  src,        }).Debug("Add address to book")        a.addAddress(addr, src)}func (a *AddrBook) addAddress(addr, src *NetAddress) {        // 验证地址是否为可路由地址        if a.routabilityStrict && !addr.Routable() {                log.Error(cmn.Fmt("Cannot add non-routable address %v", addr))                return        }        // 验证地址是否为本地节点地址        if _, ok := a.ourAddrs[addr.String()]; ok {                // Ignore our own listener address.                return        }        // 验证地址是否存在地址集中        // 如果存在:则判断该地址是否为old可靠地址、是否超过了最大buckets中。否则根据该地址已经被ka.Buckets引用的个数来随机决定是否添加到地址集中        // 如果不存在:则添加到地址集中。并标识为bucketTypeNew地址类型        ka := a.addrLookup[addr.String()]        if ka != nil {                // Already old.                if ka.isOld() {                        return                }                // Already in max new buckets.                if len(ka.Buckets) == maxNewBucketsPerAddress {                        return                }                // The more entries we have, the less likely we are to add more.                factor := int32(2 * len(ka.Buckets))                if a.rand.Int31n(factor) != 0 {                        return                }        } else {                ka = newKnownAddress(addr, src)        }        // 找到该地址在地址集的索引位置并添加        bucket := a.calcNewBucket(addr, src)        a.addToNewBucket(ka, bucket)        log.Info("Added new address ", "address:", addr, " total:", a.size())}

选择最优节点

地址簿中存储众多地址,在p2p网络中需选择最优的地址去连接 PickAddress(newBias int)函数中newBias是由pex_reactor产生的地址评分。如何计算地址分数在其他章节中再讲 根据地址评分随机选择地址可增加区块链安全性

// Pick an address to connect to with new/old bias.func (a *AddrBook) PickAddress(newBias int) *NetAddress {        a.mtx.Lock()        defer a.mtx.Unlock()        if a.size() == 0 {                return nil        }        // newBias地址分数限制在0-100分数之间        if newBias > 100 {                newBias = 100        }        if newBias < 0 {                newBias = 0        }        // Bias between new and old addresses.        oldCorrelation := math.Sqrt(float64(a.nOld)) * (100.0 - float64(newBias))        newCorrelation := math.Sqrt(float64(a.nNew)) * float64(newBias)  // 根据地址分数计算是否从addrOld或addrNew中随机选择一个地址        if (newCorrelation+oldCorrelation)*a.rand.Float64() < oldCorrelation {                // pick random Old bucket.                var bucket map[string]*knownAddress = nil                num := 0                for len(bucket) == 0 && num < oldBucketCount {                        bucket = a.addrOld[a.rand.Intn(len(a.addrOld))]                        num++                }                if num == oldBucketCount {                        return nil                }                // pick a random ka from bucket.                randIndex := a.rand.Intn(len(bucket))                for _, ka := range bucket {                        if randIndex == 0 {                                return ka.Addr                        }                        randIndex--                }                cmn.PanicSanity("Should not happen")        } else {                // pick random New bucket.                var bucket map[string]*knownAddress = nil                num := 0                for len(bucket) == 0 && num < newBucketCount {                        bucket = a.addrNew[a.rand.Intn(len(a.addrNew))]                        num++                }                if num == newBucketCount {                        return nil                }                // pick a random ka from bucket.                randIndex := a.rand.Intn(len(bucket))                for _, ka := range bucket {                        if randIndex == 0 {                                return ka.Addr                        }                        randIndex--                }                cmn.PanicSanity("Should not happen")        }        return nil}

移除一个地址

当一个地址被标记为Bad时则从地址集中移除。目前bytomd的代码版本并未调用过

func (a *AddrBook) MarkBad(addr *NetAddress) {        a.RemoveAddress(addr)}// RemoveAddress removes the address from the book.func (a *AddrBook) RemoveAddress(addr *NetAddress) {        a.mtx.Lock()        defer a.mtx.Unlock()        ka := a.addrLookup[addr.String()]        if ka == nil {                return        }        log.WithField("addr", addr).Info("Remove address from book")        a.removeFromAllBuckets(ka)}func (a *AddrBook) removeFromAllBuckets(ka *knownAddress) {        for _, bucketIdx := range ka.Buckets {                bucket := a.getBucket(ka.BucketType, bucketIdx)                delete(bucket, ka.Addr.String())        }        ka.Buckets = nil        if ka.BucketType == bucketTypeNew {                a.nNew--        } else {                a.nOld--        }        delete(a.addrLookup, ka.Addr.String())}

感谢各位的阅读,以上就是"Bytom的P2P网络地址簿结构体是怎样的"的内容了,经过本文的学习后,相信大家对Bytom的P2P网络地址簿结构体是怎样的这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

地址 地址簿 存储 网络 节点 结构 标识 分数 类型 路由 选择 验证 成功 内容 学习 之间 信息 大小 数量 文章 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 博彦科技互联网金融 互联网通讯科技 互联网科技垄断 网络科技有限互联网 狮子座互联网科技 善班互联网科技 世界互联网领先科技成果 远拓互联网科技 互联网科技公司介绍 四川狮子座互联网科技 甘肃拓世互联网科技有限公司 互联网科技实习生岗位职责 速维有品互联网信息科技(苏州)有限公司 哈尔滨屹滨互联网科技有限公司 链环科技传统企业怎么转型互联网 沐思想互联网科技有限公司 互联网与数字科技关系 山东大美朱楼互联网科技有限公司 10位科技互联网大佬 重庆科技产业互联网有哪些 互联网科技公司 腾讯阿里互联网科技 狮子座互联网科技培训 中国的互联网科技企业的崛起 电子科技大学互联网创业大赛 深圳市罗太互联网科技有限公司 东营紫格互联网科技 容联科技互联网电商 中国新注册互联网科技 平安好医生打造互联网科技医疗
0