tendermint/peer/client.go

216 lines
4.8 KiB
Go
Raw Normal View History

2014-06-18 20:48:32 -07:00
package peer
import (
2014-07-01 14:50:24 -07:00
"errors"
"sync"
"sync/atomic"
2014-07-01 14:50:24 -07:00
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/merkle"
2014-06-18 20:48:32 -07:00
)
// BUG(jae) handle peer disconnects
2014-06-24 17:28:40 -07:00
/*
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.
2014-06-24 17:28:40 -07:00
All communication amongst peers are multiplexed by "channels".
(Not the same as Go "channels")
2014-06-24 17:28:40 -07:00
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()".
2014-06-24 17:28:40 -07:00
*/
2014-06-18 20:48:32 -07:00
type Client struct {
2014-07-01 14:50:24 -07:00
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{}
erroredPeers chan peerError
stopped uint32
2014-06-18 20:48:32 -07:00
}
2014-06-24 17:28:40 -07:00
var (
2014-07-01 14:50:24 -07:00
CLIENT_STOPPED_ERROR = errors.New("Client already stopped")
CLIENT_DUPLICATE_PEER_ERROR = errors.New("Duplicate peer")
2014-06-24 17:28:40 -07:00
)
// "makePeerFn" is a factory method for generating new peers from new *Connections.
// "makePeerFn(nil)" must return a prototypical peer that represents the self "peer".
2014-06-29 00:35:16 -07:00
func NewClient(makePeerFn func(*Connection) *Peer) *Client {
2014-07-01 14:50:24 -07:00
self := makePeerFn(nil)
if self == nil {
Panicf("makePeerFn(nil) must return a prototypical peer for self")
}
pktRecvQueues := make(map[String]chan *InboundPacket)
2014-07-01 14:50:24 -07:00
for chName, _ := range self.channels {
pktRecvQueues[chName] = make(chan *InboundPacket)
2014-07-01 14:50:24 -07:00
}
c := &Client{
addrBook: nil, // TODO
targetNumPeers: 0, // TODO
makePeerFn: makePeerFn,
self: self,
pktRecvQueues: pktRecvQueues,
peers: merkle.NewIAVLTree(nil),
quit: make(chan struct{}),
erroredPeers: make(chan peerError),
stopped: 0,
2014-07-01 14:50:24 -07:00
}
// automatically start
c.start()
2014-07-01 14:50:24 -07:00
return c
2014-06-18 20:48:32 -07:00
}
func (c *Client) start() {
// Handle peer disconnects & errors
go c.peerErrorHandler()
}
2014-06-18 20:48:32 -07:00
func (c *Client) Stop() {
2014-07-01 14:50:24 -07:00
log.Infof("Stopping client")
// lock
c.peersMtx.Lock()
2014-07-01 14:50:24 -07:00
if atomic.CompareAndSwapUint32(&c.stopped, 0, 1) {
close(c.quit)
// stop each peer.
for peerValue := range c.peers.Values() {
peer := peerValue.(*Peer)
peer.stop()
2014-07-01 14:50:24 -07:00
}
// empty tree.
c.peers = merkle.NewIAVLTree(nil)
}
c.peersMtx.Unlock()
2014-07-01 14:50:24 -07:00
// unlock
2014-06-18 20:48:32 -07:00
}
2014-06-24 17:28:40 -07:00
func (c *Client) AddPeerWithConnection(conn *Connection, outgoing bool) (*Peer, error) {
2014-07-01 14:50:24 -07:00
if atomic.LoadUint32(&c.stopped) == 1 {
return nil, CLIENT_STOPPED_ERROR
}
2014-06-18 20:48:32 -07:00
2014-07-01 14:50:24 -07:00
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
}
2014-06-18 20:48:32 -07:00
go peer.start(c.pktRecvQueues, c.erroredPeers)
2014-06-18 20:48:32 -07:00
2014-07-01 14:50:24 -07:00
return peer, nil
2014-06-18 20:48:32 -07:00
}
func (c *Client) Broadcast(pkt Packet) (numSuccess, numFailure int) {
2014-07-01 14:50:24 -07:00
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() {
2014-07-01 14:50:24 -07:00
peer := v.(*Peer)
success := peer.TrySend(pkt)
log.Tracef("Broadcast for peer %v success: %v", peer, success)
if success {
numSuccess += 1
} else {
numFailure += 1
2014-07-01 14:50:24 -07:00
}
}
return
2014-06-18 20:48:32 -07:00
}
/*
Receive blocks on a channel until a message is found.
*/
func (c *Client) Receive(chName String) *InboundPacket {
2014-07-01 14:50:24 -07:00
if atomic.LoadUint32(&c.stopped) == 1 {
return nil
}
log.Tracef("Receive on [%v]", chName)
q := c.pktRecvQueues[chName]
2014-07-01 14:50:24 -07:00
if q == nil {
Panicf("Expected pktRecvQueues[%f], found none", chName)
2014-07-01 14:50:24 -07:00
}
select {
case <-c.quit:
return nil
case inPacket := <-q:
return inPacket
2014-07-01 14:50:24 -07:00
}
2014-06-18 20:48:32 -07:00
}
2014-06-29 00:35:16 -07:00
func (c *Client) Peers() merkle.Tree {
2014-07-01 14:50:24 -07:00
// lock & defer
c.peersMtx.Lock()
defer c.peersMtx.Unlock()
2014-07-01 14:50:24 -07:00
return c.peers.Copy()
// unlock deferred
2014-06-29 00:35:16 -07:00
}
2014-06-24 17:28:40 -07:00
func (c *Client) StopPeer(peer *Peer) {
2014-07-01 14:50:24 -07:00
// lock
c.peersMtx.Lock()
2014-07-01 14:50:24 -07:00
peerValue, _ := c.peers.Remove(peer.RemoteAddress())
c.peersMtx.Unlock()
2014-07-01 14:50:24 -07:00
// unlock
peer_ := peerValue.(*Peer)
if peer_ != nil {
peer_.stop()
2014-07-01 14:50:24 -07:00
}
2014-06-18 20:48:32 -07:00
}
2014-06-24 17:28:40 -07:00
func (c *Client) addPeer(peer *Peer) error {
2014-07-01 14:50:24 -07:00
addr := peer.RemoteAddress()
// lock & defer
c.peersMtx.Lock()
defer c.peersMtx.Unlock()
2014-07-01 14:50:24 -07:00
if c.stopped == 1 {
return CLIENT_STOPPED_ERROR
}
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 CLIENT_DUPLICATE_PEER_ERROR
}
// unlock deferred
2014-06-18 20:48:32 -07:00
}
func (c *Client) peerErrorHandler() {
for {
select {
case <-c.quit:
return
case errPeer := <-c.erroredPeers:
// TODO do something
c.StopPeer(errPeer.peer)
return
}
}
}