rpcclient

rpc client 模块

这个模块是进行rpc调用的入口模块,是为了帮助btcd的客户端程序实现而设计的模块,注意btcctl并没有用此模块,它直接用的是btcjson.

这个模块式在btcjson上面再次封装,方便调用

创建client

connCfg := &rpcclient.ConnConfig{
		Host:         "192.168.124.13:18334",
		User:         "bai",
		Pass:         "bai",
		HTTPPostMode: true, // Bitcoin core only supports HTTP POST mode
		DisableTLS:   false, // Bitcoin core does not provide TLS by default,
		Certificates:[]byte(`-----BEGIN CERTIFICATE-----
MIICTzCCAbGgAwIBAgIQLZMB/bcvEeMIuQSobOhvcTAKBggqhkjOPQQDBDAhMREw
DwYDVQQKEwhnZW5jZXJ0czEMMAoGA1UEAxMDZXRoMB4XDTE4MTEyNzA2MzYxMFoX
DTI4MTEyNTA2MzYxMFowITERMA8GA1UEChMIZ2VuY2VydHMxDDAKBgNVBAMTA2V0
aDCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEAD5A5+3tRK2UDwmIUYfkWPzXtS67
zjgToegCMGkgzEwWrOJvaPj2uFy95v5hsozmfyh5cZY1o2FHOGrYnVNQcxE+AW9Z
Oofsgz+4SPY7W8bTkZka6670ejZ1EMfPwFio7ObyVEZt4eFe7xVQ9pJEwg3XWM2c
YdT6xpn0gQAQ9canuWa8o4GHMIGEMA4GA1UdDwEB/wQEAwICpDAPBgNVHRMBAf8E
BTADAQH/MGEGA1UdEQRaMFiCA2V0aIIJbG9jYWxob3N0hwR/AAABhxAAAAAAAAAA
AAAAAAAAAAABhwTAqHwNhwQKAABkhxD+gAAAAAAAADrVR//+ALeGhxD+gAAAAAAA
ALj9/f/+anOwMAoGCCqGSM49BAMEA4GLADCBhwJBU0neT5MZvmRwXnUXUdorOojt
B2sqIW1eZkb6xZzZTnbjLaU+EFdMsdHYxXcM9eAHSbtKbRmsCh+DBL7sWuMNiJYC
QgFRgh2wZofvEBFVLXGTgawWv8qIqgneOflIcl4cukbA7PLLZ5nfHCO/iC7oP6JH
bkGHJJz1CYKS12HU4jzSZYq7eQ==
-----END CERTIFICATE-----`),
	}
	// Notice the notification parameter is nil since notifications are
	// not supported in HTTP POST mode.
	client, err := rpcclient.New(connCfg, nil)
	if err != nil {
		log.Fatal(err)
	}

如果要调用在另一台电脑上运行的btcd,就必须指定正确的证书,否则无法建立有效连接.

http 调用分析

以GetBlockCount为例进行分析

func (c *Client) GetBlockCountAsync() FutureGetBlockCountResult {
	cmd := btcjson.NewGetBlockCountCmd()
	return c.sendCmd(cmd)
}

// GetBlockCount returns the number of blocks in the longest block chain.
func (c *Client) GetBlockCount() (int64, error) {
	return c.GetBlockCountAsync().Receive()
}

真正通用部分的处理都在infrastructure.go中,该文件处理了如何发送和派发消息,兼顾http和websocket两种连接方式.

// start begins processing input and output messages.
func (c *Client) start() {
	log.Tracef("Starting RPC client %s", c.config.Host)

	// Start the I/O processing handlers depending on whether the client is
	// in HTTP POST mode or the default websocket mode.
	if c.config.HTTPPostMode {
		c.wg.Add(1)
		go c.sendPostHandler()
	} else {
		c.wg.Add(3)
		go func() {
			if c.ntfnHandlers != nil {
				if c.ntfnHandlers.OnClientConnected != nil {
					c.ntfnHandlers.OnClientConnected()
				}
			}
			c.wg.Done()
		}()
		go c.wsInHandler()
		go c.wsOutHandler()
	}
}

启动过程就可以看出来,对于http就是启动一个接受上层发起post请求的chan就可以了.
但是websocket是两个,一个是接受上层发送消息请求,一个是接受服务器主动发送来的消息. 也就是说在websocket方式下,所有的消息走的都是websocket消息.

相对http来说,收到的消息明确知道派发到哪个Response,但是websocket就必须通过id来处理.因此处理http消息的是handleSendPostMessage,而处理websocket消息的是handleMessage

因此最终在handleSendPostMessage中FutureGetBlockCountResult的Receive收到数据,并进行解析,然后返回.

websocket连接的事件推送

// handleMessage is the main handler for incoming notifications and responses.
func (c *Client) handleMessage(msg []byte) {
	// Attempt to unmarshal the message as either a notification or
	// response.
	var in inMessage
	json.Unmarshal(msg, &in)
 
	// JSON-RPC 1.0 notifications are requests with a null id.
	if in.ID == nil {
		ntfn := in.rawNotification
		// Deliver the notification.
		log.Tracef("Received notification [%s]", in.Method)
		c.handleNotification(in.rawNotification)
		return
	}

	id := uint64(*in.ID)
	log.Tracef("Received response for id %d (result %s)", id, in.Result)
	request := c.removeRequest(id)

	// Nothing more to do if there is no request associated with this reply.
	if request == nil || request.responseChan == nil {
		log.Warnf("Received unexpected reply: %s (id %d)", in.Result,
			id)
		return
	}

	// Since the command was successful, examine it to see if it's a
	// notification, and if is, add it to the notification state so it
	// can automatically be re-established on reconnect.
	c.trackRegisteredNtfns(request.cmd)

	// Deliver the response.
	result, err := in.rawResponse.result()
	request.responseChan <- &response{result: result, err: err}
}

服务器的主动推送的Notification,消息中是没有ID一项的,如果是客户端的请求消息,服务器推送消息肯定包含ID.

以下是服务器可以主动推送的事件.

const (
	// BlockConnectedNtfnMethod is the legacy, deprecated method used for
	// notifications from the chain server that a block has been connected.
	//
	// NOTE: Deprecated. Use FilteredBlockConnectedNtfnMethod instead.
	BlockConnectedNtfnMethod = "blockconnected"

	// BlockDisconnectedNtfnMethod is the legacy, deprecated method used for
	// notifications from the chain server that a block has been
	// disconnected.
	//
	// NOTE: Deprecated. Use FilteredBlockDisconnectedNtfnMethod instead.
	BlockDisconnectedNtfnMethod = "blockdisconnected"

	// FilteredBlockConnectedNtfnMethod is the new method used for
	// notifications from the chain server that a block has been connected.
	FilteredBlockConnectedNtfnMethod = "filteredblockconnected"

	// FilteredBlockDisconnectedNtfnMethod is the new method used for
	// notifications from the chain server that a block has been
	// disconnected.
	FilteredBlockDisconnectedNtfnMethod = "filteredblockdisconnected"

	// RecvTxNtfnMethod is the legacy, deprecated method used for
	// notifications from the chain server that a transaction which pays to
	// a registered address has been processed.
	//
	// NOTE: Deprecated. Use RelevantTxAcceptedNtfnMethod and
	// FilteredBlockConnectedNtfnMethod instead.
	RecvTxNtfnMethod = "recvtx"

	// RedeemingTxNtfnMethod is the legacy, deprecated method used for
	// notifications from the chain server that a transaction which spends a
	// registered outpoint has been processed.
	//
	// NOTE: Deprecated. Use RelevantTxAcceptedNtfnMethod and
	// FilteredBlockConnectedNtfnMethod instead.
	RedeemingTxNtfnMethod = "redeemingtx"

	// RescanFinishedNtfnMethod is the legacy, deprecated method used for
	// notifications from the chain server that a legacy, deprecated rescan
	// operation has finished.
	//
	// NOTE: Deprecated. Not used with rescanblocks command.
	RescanFinishedNtfnMethod = "rescanfinished"

	// RescanProgressNtfnMethod is the legacy, deprecated method used for
	// notifications from the chain server that a legacy, deprecated rescan
	// operation this is underway has made progress.
	//
	// NOTE: Deprecated. Not used with rescanblocks command.
	RescanProgressNtfnMethod = "rescanprogress"

	// TxAcceptedNtfnMethod is the method used for notifications from the
	// chain server that a transaction has been accepted into the mempool.
	TxAcceptedNtfnMethod = "txaccepted"

	// TxAcceptedVerboseNtfnMethod is the method used for notifications from
	// the chain server that a transaction has been accepted into the
	// mempool.  This differs from TxAcceptedNtfnMethod in that it provides
	// more details in the notification.
	TxAcceptedVerboseNtfnMethod = "txacceptedverbose"

	// RelevantTxAcceptedNtfnMethod is the new method used for notifications
	// from the chain server that inform a client that a transaction that
	// matches the loaded filter was accepted by the mempool.
	RelevantTxAcceptedNtfnMethod = "relevanttxaccepted"
)