tendermint/peer/client.go

210 lines
4.9 KiB
Go

package peer
import (
"errors"
"sync"
"sync/atomic"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/merkle"
)
/*
A client is half of a p2p system.
It can reach out to the network and establish connections with other peers.
A client doesn't listen for incoming connections -- that's done by the server.
All communication amongst peers are multiplexed by "channels".
(Not the same as Go "channels")
To send a message, encapsulate it into a "Packet" and send it to each peer.
You can find all connected and active peers by iterating over ".Peers()".
".Broadcast()" is provided for convenience, but by iterating over
the peers manually the caller can decide which subset receives a message.
Incoming messages are received by calling ".Receive()".
*/
type Client struct {
addrBook *AddrBook
targetNumPeers int
makePeerFn func(*Connection) *Peer
self *Peer
pktRecvQueues map[String]chan *InboundPacket
peersMtx sync.Mutex
peers merkle.Tree // addr -> *Peer
quit chan struct{}
stopped uint32
}
var (
ErrClientStopped = errors.New("Client already stopped")
ErrClientDuplicatePeer = errors.New("Duplicate peer")
)
// "makePeerFn" is a factory method for generating new peers from new *Connections.
// "makePeerFn(nil)" must return a prototypical peer that represents the self "peer".
func NewClient(makePeerFn func(*Connection) *Peer) *Client {
self := makePeerFn(nil)
if self == nil {
Panicf("makePeerFn(nil) must return a prototypical peer for self")
}
pktRecvQueues := make(map[String]chan *InboundPacket)
for chName, _ := range self.channels {
pktRecvQueues[chName] = make(chan *InboundPacket)
}
c := &Client{
addrBook: nil, // TODO
targetNumPeers: 0, // TODO
makePeerFn: makePeerFn,
self: self,
pktRecvQueues: pktRecvQueues,
peers: merkle.NewIAVLTree(nil),
quit: make(chan struct{}),
stopped: 0,
}
// automatically start
c.start()
return c
}
func (c *Client) start() {
// Handle PEX messages
// TODO: hmm
// go peerExchangeHandler(c)
}
func (c *Client) Stop() {
log.Infof("Stopping client")
// lock
c.peersMtx.Lock()
if atomic.CompareAndSwapUint32(&c.stopped, 0, 1) {
close(c.quit)
// stop each peer.
for peerValue := range c.peers.Values() {
peer := peerValue.(*Peer)
peer.stop()
}
// empty tree.
c.peers = merkle.NewIAVLTree(nil)
}
c.peersMtx.Unlock()
// unlock
}
func (c *Client) AddPeerWithConnection(conn *Connection, outgoing bool) (*Peer, error) {
if atomic.LoadUint32(&c.stopped) == 1 {
return nil, ErrClientStopped
}
log.Infof("Adding peer with connection: %v, outgoing: %v", conn, outgoing)
peer := c.makePeerFn(conn)
peer.outgoing = outgoing
err := c.addPeer(peer)
if err != nil {
return nil, err
}
go peer.start(c.pktRecvQueues, c.StopPeerForError)
return peer, nil
}
func (c *Client) Broadcast(pkt Packet) (numSuccess, numFailure int) {
if atomic.LoadUint32(&c.stopped) == 1 {
return
}
log.Tracef("Broadcast on [%v] len: %v", pkt.Channel, len(pkt.Bytes))
for v := range c.peers.Values() {
peer := v.(*Peer)
success := peer.TryQueue(pkt)
log.Tracef("Broadcast for peer %v success: %v", peer, success)
if success {
numSuccess += 1
} else {
numFailure += 1
}
}
return
}
/*
Receive blocks on a channel until a message is found.
*/
func (c *Client) Receive(chName String) *InboundPacket {
if atomic.LoadUint32(&c.stopped) == 1 {
return nil
}
log.Tracef("Receive on [%v]", chName)
q := c.pktRecvQueues[chName]
if q == nil {
Panicf("Expected pktRecvQueues[%f], found none", chName)
}
select {
case <-c.quit:
return nil
case inPacket := <-q:
return inPacket
}
}
func (c *Client) Peers() merkle.Tree {
// lock & defer
c.peersMtx.Lock()
defer c.peersMtx.Unlock()
return c.peers.Copy()
// unlock deferred
}
// Disconnect from a peer due to external error.
// TODO: make record depending on reason.
func (c *Client) StopPeerForError(peer *Peer, reason interface{}) {
log.Infof("%v errored: %v", peer, reason)
c.StopPeer(peer, false)
}
// Disconnect from a peer.
// If graceful is true, last message sent is a disconnect message.
// TODO: handle graceful disconnects.
func (c *Client) StopPeer(peer *Peer, graceful bool) {
// lock
c.peersMtx.Lock()
peerValue, _ := c.peers.Remove(peer.RemoteAddress())
c.peersMtx.Unlock()
// unlock
peer_ := peerValue.(*Peer)
if peer_ != nil {
peer_.stop()
}
}
func (c *Client) addPeer(peer *Peer) error {
addr := peer.RemoteAddress()
// lock & defer
c.peersMtx.Lock()
defer c.peersMtx.Unlock()
if c.stopped == 1 {
return ErrClientStopped
}
if !c.peers.Has(addr) {
log.Tracef("Actually putting addr: %v, peer: %v", addr, peer)
c.peers.Put(addr, peer)
return nil
} else {
// ignore duplicate peer for addr.
log.Infof("Ignoring duplicate peer for addr %v", addr)
return ErrClientDuplicatePeer
}
// unlock deferred
}