simplify server logic

- get rid of dialLoop entirely
- get rid of peerConnect channel in favour of simple function call
- server handles the peerSelector hook for prospective outbound peers
- no need for NoDial anymore really
- changed how peer is initialised
This commit is contained in:
zelig 2015-01-06 18:20:07 +00:00
parent 3d41559fda
commit 72deabeb42

View file

@ -67,7 +67,7 @@ type Server struct {
// Hook for testing. This is useful because we can inhibit // Hook for testing. This is useful because we can inhibit
// the whole protocol stack. // the whole protocol stack.
newPeerFunc peerFunc // newPeerFunc peerFunc
lock sync.RWMutex lock sync.RWMutex
running bool running bool
@ -79,7 +79,6 @@ type Server struct {
quit chan struct{} quit chan struct{}
wg sync.WaitGroup wg sync.WaitGroup
peerConnect chan *peerAddr
peerDisconnect chan *Peer peerDisconnect chan *Peer
} }
@ -95,7 +94,7 @@ type NAT interface {
type peerFunc func(srv *Server, c net.Conn, dialAddr *peerAddr) *Peer type peerFunc func(srv *Server, c net.Conn, dialAddr *peerAddr) *Peer
// Peers returns all connected peers. // Peers returns all currently connected peers.
func (srv *Server) Peers() (peers []*Peer) { func (srv *Server) Peers() (peers []*Peer) {
srv.lock.RLock() srv.lock.RLock()
defer srv.lock.RUnlock() defer srv.lock.RUnlock()
@ -107,33 +106,18 @@ func (srv *Server) Peers() (peers []*Peer) {
return return
} }
// GetActivePeers returns addresses of all connected peers.
func (srv *Server) GetActivePeers() (peers []*peerAddr) {
for _, peer := range srv.Peers() {
if peer != nil {
peer.infolock.Lock()
addr := peer.listenAddr
peer.infolock.Unlock()
// filter out this peer and peers that are not listening or
// have not completed the handshake.
// TODO: track previously sent peers and exclude them as well.
if addr == nil {
continue
}
peers = append(peers, addr)
}
}
return peers
}
// GetPeers returns addresses near target if given or supported by the client // GetPeers returns addresses near target if given or supported by the client
// or falls back to all actively connected peers // or falls back to all actively connected peers
func (srv *Server) GetPeers(target ...[]byte) (peers []*peerAddr) { func (srv *Server) GetPeers(target ...[]byte) []*peerAddr {
if len(target) == 1 { // delegate to selector if len(target) == 1 { // delegate to selector
srv.PeerSelector.GetPeers(target[0]) return ActiveAddresses(srv.PeerSelector.GetPeers(target[0])...)
return peers
} else { } else {
return srv.GetActivePeers() // in fact it is not clear why the selector would not want to reply to this case as well
var peers []peerInfo
for _, peer := range srv.Peers() {
peers = append(peers, peer)
}
return ActiveAddresses(peers...)
} }
} }
@ -152,7 +136,7 @@ func (srv *Server) SuggestPeer(addr string, pubkey []byte) error {
srvlog.Errorf("couldn't resolve %s:", addr, err) srvlog.Errorf("couldn't resolve %s:", addr, err)
return err return err
} }
peerAddr := &peerAddr{IP: netaddr.IP, Port: uint64(netaddr.Port), Pubkey: pubkey} peerAddr := &peerAddr{netaddr.IP, uint64(netaddr.Port), pubkey}
return srv.AddPeer(peerAddr) return srv.AddPeer(peerAddr)
} }
@ -161,15 +145,43 @@ func (srv *Server) SuggestPeer(addr string, pubkey []byte) error {
// to decide if it is a worthwhile connection // to decide if it is a worthwhile connection
func (srv *Server) AddPeer(addr *peerAddr) (err error) { func (srv *Server) AddPeer(addr *peerAddr) (err error) {
// need to look up nodeID first // need to look up nodeID first
peer := &peerRecord{addr: addr} peer := &Peer{
if err = srv.PeerSelector.AddPeer(peer); err == nil { dialAddr: addr,
srvlog.Infof("peer %v accepted by peer selection", peer) lastActiveC: make(chan time.Time),
lastActive: time.Now().Add(-24 * time.Hour),
}
if srv.PeerSelector.AddPeer(peer) {
srvlog.Infof("peer %v accepted by peer selection", addr)
err = srv.dialPeer(peer)
} else { } else {
srvlog.Infof("peer %v rejected by peer selection", peer) srvlog.Infof("peer %v rejected by peer selection", addr)
} }
return return
} }
func (srv *Server) dialPeer(peer *Peer) (err error) {
timeout := time.After(5 * time.Second)
select {
case <-timeout:
err = fmt.Errorf("Too many connections. No slot available")
case slot := <-srv.peerSlots: // there is a slot available
srvlog.Debugf("Dialing %v (slot %d)\n", peer.dialAddr, slot)
conn, dialErr := srv.Dialer.Dial(peer.dialAddr.Network(), peer.dialAddr.String())
if dialErr != nil {
err = fmt.Errorf("Dial error: %v", dialErr)
srvlog.Errorln(err)
srv.peerSlots <- slot
return
}
peer.slot = slot
peer.connect(srv, conn)
srv.wg.Add(1)
go srv.addPeer(peer)
}
return
}
// Broadcast sends an RLP-encoded message to all connected peers. // Broadcast sends an RLP-encoded message to all connected peers.
// This method is deprecated and will be removed later. // This method is deprecated and will be removed later.
func (srv *Server) Broadcast(protocol string, code uint64, data ...interface{}) { func (srv *Server) Broadcast(protocol string, code uint64, data ...interface{}) {
@ -211,11 +223,10 @@ func (srv *Server) Start() (err error) {
srv.quit = make(chan struct{}) srv.quit = make(chan struct{})
srv.peers = make([]*Peer, srv.MaxPeers) srv.peers = make([]*Peer, srv.MaxPeers)
srv.peerSlots = make(chan int, srv.MaxPeers) srv.peerSlots = make(chan int, srv.MaxPeers)
srv.peerConnect = make(chan *peerAddr, outboundAddressPoolSize)
srv.peerDisconnect = make(chan *Peer) srv.peerDisconnect = make(chan *Peer)
if srv.newPeerFunc == nil { // if srv.newPeerFunc == nil {
srv.newPeerFunc = newServerPeer // srv.newPeerFunc = newServerPeer
} // }
if srv.Blacklist == nil { if srv.Blacklist == nil {
srv.Blacklist = NewBlacklist() srv.Blacklist = NewBlacklist()
} }
@ -228,10 +239,10 @@ func (srv *Server) Start() (err error) {
return err return err
} }
} }
if !srv.NoDial { // if !srv.NoDial {
srv.wg.Add(1) // srv.wg.Add(1)
go srv.dialLoop() // go srv.dialLoop()
} // }
if srv.NoDial && srv.ListenAddr == "" { if srv.NoDial && srv.ListenAddr == "" {
srvlog.Warnln("I will be kind-of useless, neither dialing nor listening.") srvlog.Warnln("I will be kind-of useless, neither dialing nor listening.")
} }
@ -319,8 +330,10 @@ func (srv *Server) listenLoop() {
srv.peerSlots <- slot srv.peerSlots <- slot
return return
} }
srvlog.Debugf("Accepted conn %v (slot %d)\n", conn.RemoteAddr(), slot) srvlog.Debugf("Accepted conn %v (slot %d) - peer selector check after handshake", conn.RemoteAddr(), slot)
srv.addPeer(conn, nil, slot) peer := &Peer{slot: slot}
peer.connect(srv, conn)
srv.addPeer(peer)
case <-srv.quit: case <-srv.quit:
return return
} }
@ -366,77 +379,22 @@ func (srv *Server) removePortMapping(port int) {
srv.NAT.DeletePortMapping("tcp", port, port) srv.NAT.DeletePortMapping("tcp", port, port)
} }
func (srv *Server) dialLoop() {
defer srv.wg.Done()
var (
suggest chan *peerAddr
slot *int
slots = srv.peerSlots
)
for {
select {
case i := <-slots:
// we need a peer in slot i, slot reserved
slot = &i
// now we can watch for candidate peers in the next loop
suggest = srv.peerConnect
// do not consume more until candidate peer is found
slots = nil
case desc := <-suggest:
// candidate peer found, will dial out asyncronously
// if connection fails slot will be released
srvlog.Infof("dial %v (%v)", desc, *slot)
go srv.dialPeer(desc, *slot)
// we can watch if more peers needed in the next loop
slots = srv.peerSlots
// until then we dont care about candidate peers
suggest = nil
case <-srv.quit:
// give back the currently reserved slot
if slot != nil {
srv.peerSlots <- *slot
}
return
}
}
}
// connect to peer via dial out
func (srv *Server) dialPeer(desc *peerAddr, slot int) {
srvlog.Debugf("Dialing %v (slot %d)\n", desc, slot)
conn, err := srv.Dialer.Dial(desc.Network(), desc.String())
if err != nil {
srvlog.Errorf("Dial error: %v", err)
srv.peerSlots <- slot
return
}
go srv.addPeer(conn, desc, slot)
}
// creates the new peer object and inserts it into its slot // creates the new peer object and inserts it into its slot
func (srv *Server) addPeer(conn net.Conn, desc *peerAddr, slot int) *Peer { func (srv *Server) addPeer(peer *Peer) {
srv.lock.Lock() srv.lock.Lock()
defer srv.lock.Unlock() defer srv.lock.Unlock()
if !srv.running { srvlog.Debugf("Add peer %v (slot %v)\n", peer, peer.slot)
conn.Close() srv.peers[peer.slot] = peer
srv.peerSlots <- slot // release slot
return nil
}
peer := srv.newPeerFunc(srv, conn, desc)
peer.slot = slot
srv.peers[slot] = peer
srv.peerCount++ srv.peerCount++
go func() { peer.loop(); srv.peerDisconnect <- peer }() go func() { peer.loop(); srv.peerDisconnect <- peer }()
return peer return
} }
// removes peer: sending disconnect msg, stop peer, remove rom list/table, release slot // removes peer: sending disconnect msg, stop peer, remove rom list/table, release slot
func (srv *Server) removePeer(peer *Peer) { func (srv *Server) removePeer(peer *Peer) {
srv.lock.Lock() srv.lock.Lock()
defer srv.lock.Unlock() defer srv.lock.Unlock()
srvlog.Debugf("Removing %v (slot %v)\n", peer, peer.slot) srvlog.Debugf("Remove peer %v (slot %v)\n", peer, peer.slot)
if srv.peers[peer.slot] != peer { if srv.peers[peer.slot] != peer {
srvlog.Warnln("Invalid peer to remove:", peer) srvlog.Warnln("Invalid peer to remove:", peer)
return return