Published: 2019-08-26


btcd 是一个golang实现的比特币全节点。


基于git代码版本: 16327141da8ce4b46b5bac57ba01352943465d9e

Table of Contents

1 connmgr模块

connmgr用来处理节点的连接,定义了一个 ConnManager ,用来管理request connect的连接、断开、失败、重试、限制最大连接数、重试次数等情况。

1.1 主要结构

// ConnManager provides a manager to handle network connections.
type ConnManager struct {
        // The following variables must only be used atomically.
        connReqCount uint64  // 管理的连接数
        start        int32  // 启动标识
        stop         int32 // 停止标志

        cfg            Config  // 配置
        wg             sync.WaitGroup  // 并发连接管理
        failedAttempts uint64  // 记录失败次数,如果成功连接则归0
        requests       chan interface{}  // 用于消息传递
        quit           chan struct{} // 用于退出

// ConnReq is the connection request to a network address. If permanent, the
// connection will be retried on disconnection.
type ConnReq struct {
        // The following variables must only be used atomically.
        id uint64  // 连接id,采用connReqCoount +1 的方式生成

        Addr      net.Addr // 连接地址
        Permanent bool  // 是否是持久连接

        conn       net.Conn
        state      ConnState // 记录该连接的状态
        stateMtx   sync.RWMutex
        retryCount uint32   // 记录持久连接的失败重试次数,如果成功连接则归0

// Config holds the configuration options related to the connection manager.
type Config struct {
        // Listeners defines a slice of listeners for which the connection
        // manager will take ownership of and accept connections.  When a
        // connection is accepted, the OnAccept handler will be invoked with the
        // connection.  Since the connection manager takes ownership of these
        // listeners, they will be closed when the connection manager is
        // stopped.
        // This field will not have any effect if the OnAccept field is not
        // also specified.  It may be nil if the caller does not wish to listen
        // for incoming connections.
        Listeners []net.Listener

        // OnAccept is a callback that is fired when an inbound connection is
        // accepted.  It is the caller's responsibility to close the connection.
        // Failure to close the connection will result in the connection manager
        // believing the connection is still active and thus have undesirable
        // side effects such as still counting toward maximum connection limits.
        // This field will not have any effect if the Listeners field is not
        // also specified since there couldn't possibly be any accepted
        // connections in that case.
        OnAccept func(net.Conn)

        // TargetOutbound is the number of outbound network connections to
        // maintain. Defaults to 8.
        TargetOutbound uint32

        // RetryDuration is the duration to wait before retrying connection
        // requests. Defaults to 5s.
        RetryDuration time.Duration

        // OnConnection is a callback that is fired when a new outbound
        // connection is established.
        OnConnection func(*ConnReq, net.Conn)

        // OnDisconnection is a callback that is fired when an outbound
        // connection is disconnected.
        OnDisconnection func(*ConnReq)

        // GetNewAddress is a way to get an address to make a network connection
        // to.  If nil, no new connections will be made automatically.
        GetNewAddress func() (net.Addr, error)

        // Dial connects to the address on the named network. It cannot be nil.
        Dial func(net.Addr) (net.Conn, error)

1.2 主要方法

1.2.1 start

1. go connHandler()
2. go listenHandler()
3. go NewConnReq()

1.2.2 connHandler

pending map: 记录pending状态的连接信息
conns map : 记录连接状态的连接信息

1. req := <-cm.requests:
   1. registerPending  表示 有待发生待连接请求
      1.记录该请求到一个pending map中
   2. handleConnected  表示 连接建立
      1.确保在pending map中添加过该连接
      2.pengding map删除条目, conns map中添加连接条目
      3.调用manager config配置的OnConnection方法

   3. handleDisconnected 表示 连接断开
      1.确保在pending或者conns map中添加过该连接,并删除相应条目
      2.调用manager config配置的OnDisconnection方法
      3.根据 该断开的消息 是否需要重试分开处理
        1. 不需要的话,更新状态就结束
        2. 需要的话,在一定条件下将连接加到pending map里,然后调用handleFailedConn
   4. handleFailed 表示 失败情况
      1.确保在pending map中添加过该连接

2. <-cm.quit

1.2.3 handleFailedConn


1.2.4 listenHandler


1.2.5 NewConnReq


1.2.6 Connect

如果(根据连接id判断)该地址是个新连接,那么设置其id,然后注册到pending map连接里。
调用config里的Dial方法,连接到addr, 如果成功则发送handleConnected方法, 失败则发送handleFailed方法到cm.requests, 由connHandler处理

1.2.7 Disconnect


1.2.8 Stop


1.3 消息传递简图

只有一个cm.requests channel,通过传递不同的msg来让connhandler处理,整体还是比较简明清晰,故不作图了。

2 End

Author: Nisen