catalog
swarm listen
call stack
github.com/libp2p/go-libp2p-transport-upgrader.(*listener).Accept at listener.go:144
github.com/libp2p/go-libp2p-swarm.(*Swarm).AddListenAddr.func2 at swarm_listen.go:76
runtime.goexit at asm_amd64.s:1357
- Async stack trace
github.com/libp2p/go-libp2p-swarm.(*Swarm).AddListenAddr at swarm_listen.go:67
First get tpt (transport) by TransportForListening(), and then listen in the tpt.
Once there is inbound connection, add it to conns in swarm and block to recv stream.
// AddListenAddr tells the swarm to listen on a single address. Unlike Listen,
// this method does not attempt to filter out bad addresses.
func (s *Swarm) AddListenAddr(a ma.Multiaddr) error {
tpt := s.TransportForListening(a)
if tpt == nil {
return ErrNoTransport
}
list, err := tpt.Listen(a)
if err != nil {
return err
}
s.listeners.Lock()
if s.listeners.m == nil {
s.listeners.Unlock()
list.Close()
return ErrSwarmClosed
}
s.refs.Add(1)
s.listeners.m[list] = struct{}{}
s.listeners.Unlock()
maddr := list.Multiaddr()
// signal to our notifiees on successful conn.
s.notifyAll(func(n network.Notifiee) {
n.Listen(s, maddr)
})
go func() {
defer func() {
list.Close()
s.listeners.Lock()
delete(s.listeners.m, list)
s.listeners.Unlock()
s.refs.Done()
}()
for {
c, err := list.Accept()
if err != nil {
if s.ctx.Err() == nil {
log.Errorf("swarm listener accept error: %s", err)
}
return
}
log.Debugf("swarm listener accepted connection: %s", c)
s.refs.Add(1)
go func() {
defer s.refs.Done()
_, err := s.addConn(c, network.DirInbound)
if err != nil {
// Probably just means that the swarm has been closed.
log.Warningf("add conn failed: ", err)
return
}
}()
}
}()
return nil
}
// Accept accepts a connection.
func (l *listener) Accept() (transport.CapableConn, error) {
for c := range l.incoming {
// Could have been sitting there for a while.
if !c.IsClosed() {
return c, nil
}
}
return nil, l.err
}
transport listen
call stack
net.(*TCPListener).Accept at tcpsock.go:257
github.com/multiformats/go-multiaddr-net.(*maListener).Accept at net.go:243
<autogenerated>:2
github.com/libp2p/go-tcp-transport.(*lingerListener).Accept at tcp.go:42
github.com/libp2p/go-libp2p-transport-upgrader.(*listener).handleIncoming at listener.go:77
runtime.goexit at asm_amd64.s:1357
- Async stack trace
github.com/libp2p/go-libp2p-transport-upgrader.(*Upgrader).UpgradeListener at upgrader.go:47
github.com/libp2p/go-tcp-transport.(*TcpTransport).Listen at tcp.go:129
github.com/libp2p/go-libp2p-swarm.(*Swarm).AddListenAddr at swarm_listen.go:45
github.com/libp2p/go-libp2p-swarm.(*Swarm).Listen at swarm_listen.go:17
github.com/libp2p/go-libp2p/config.(*Config).NewNode at config.go:183
github.com/libp2p/go-libp2p.NewWithoutDefaults at libp2p.go:71
github.com/libp2p/go-libp2p.New at libp2p.go:57
main.makeBasicHost at main.go:57
main.main at main.go:99
runtime.main at proc.go:203
runtime.goexit at asm_amd64.s:1357
- Async stack trace
runtime.rt0_go at asm_amd64.s:220
If reuse port, invoke reuse transport to listen. It will be discussed in next blob.
func (t *TcpTransport) maListen(laddr ma.Multiaddr) (manet.Listener, error) {
if t.UseReuseport() {
return t.reuse.Listen(laddr)
}
return manet.Listen(laddr)
}
// Listen listens on the given multiaddr.
func (t *TcpTransport) Listen(laddr ma.Multiaddr) (transport.Listener, error) {
list, err := t.maListen(laddr)
if err != nil {
return nil, err
}
list = &lingerListener{list, 0}
return t.Upgrader.UpgradeListener(t, list), nil
}
Wait to accept until new inbound connection is coming. and then upgrade to setup muxer and security, The upgrade process will be shown in next blob.
UpgradeListener will retun a upgrader.listener object. So swarm can accept connection via list.Accept().
// UpgradeListener upgrades the passed multiaddr-net listener into a full libp2p-transport listener.
func (u *Upgrader) UpgradeListener(t transport.Transport, list manet.Listener) transport.Listener {
ctx, cancel := context.WithCancel(context.Background())
l := &listener{
Listener: list,
upgrader: u,
transport: t,
threshold: newThreshold(AcceptQueueLength),
incoming: make(chan transport.CapableConn),
cancel: cancel,
ctx: ctx,
}
go l.handleIncoming()
return l
}
// handles inbound connections.
//
// This function does a few interesting things that should be noted:
//
// 1. It logs and discards temporary/transient errors (errors with a Temporary()
// function that returns true).
// 2. It stops accepting new connections once AcceptQueueLength connections have
// been fully negotiated but not accepted. This gives us a basic backpressure
// mechanism while still allowing us to negotiate connections in parallel.
func (l *listener) handleIncoming() {
var wg sync.WaitGroup
defer func() {
// make sure we're closed
l.Listener.Close()
if l.err == nil {
l.err = fmt.Errorf("listener closed")
}
wg.Wait()
close(l.incoming)
}()
var catcher tec.TempErrCatcher
for l.ctx.Err() == nil {
maconn, err := l.Listener.Accept()
if err != nil {
// Note: function may pause the accept loop.
if catcher.IsTemporary(err) {
log.Infof("temporary accept error: %s", err)
continue
}
l.err = err
return
}
// The go routine below calls Release when the context is
// canceled so there's no need to wait on it here.
l.threshold.Wait()
log.Debugf("listener %s got connection: %s <---> %s",
l,
maconn.LocalMultiaddr(),
maconn.RemoteMultiaddr())
wg.Add(1)
go func() {
defer wg.Done()
ctx, cancel := context.WithTimeout(l.ctx, transport.AcceptTimeout)
defer cancel()
conn, err := l.upgrader.UpgradeInbound(ctx, l.transport, maconn)
if err != nil {
// Don't bother bubbling this up. We just failed
// to completely negotiate the connection.
log.Debugf("accept upgrade error: %s (%s <--> %s)",
err,
maconn.LocalMultiaddr(),
maconn.RemoteMultiaddr())
return
}
log.Debugf("listener %s accepted connection: %s", l, conn)
// This records the fact that the connection has been
// setup and is waiting to be accepted. This call
// *never* blocks, even if we go over the threshold. It
// simply ensures that calls to Wait block while we're
// over the threshold.
l.threshold.Acquire()
defer l.threshold.Release()
select {
case l.incoming <- conn:
case <-ctx.Done():
if l.ctx.Err() == nil {
// Listener *not* closed but the accept timeout expired.
log.Warningf("listener dropped connection due to slow accept")
}
// Wait on the context with a timeout. This way,
// if we stop accepting connections for some reason,
// we'll eventually close all the open ones
// instead of hanging onto them.
conn.Close()
}
}()
}
}
You may find many listener in the process, Let’s clarify them.
Listener interface
// A Listener is a generic network listener for stream-oriented protocols.
// it uses an embedded net.Listener, overriding net.Listener.Accept to
// return a Conn and providing Multiaddr.
type Listener interface {
// Accept waits for and returns the next connection to the listener.
// Returns a Multiaddr friendly Conn
Accept() (Conn, error)
// Close closes the listener.
// Any blocked Accept operations will be unblocked and return errors.
Close() error
// Multiaddr returns the listener's (local) Multiaddr.
Multiaddr() ma.Multiaddr
// Addr returns the net.Listener's network address.
Addr() net.Addr
}
implements
lingerListener
maListener
RelayListener
tcpreuse.listener
websocket.listener
The specified implements will be disscussed in next blob.
lingerListener
It seems that lingerListener is used to wait connection with timeout. but is not used in current version.
type lingerListener struct {
manet.Listener
sec int
}
func (ll *lingerListener) Accept() (manet.Conn, error) {
c, err := ll.Listener.Accept()
if err != nil {
return nil, err
}
tryLinger(c, ll.sec)
return c, nil
}
maListener
maListener is listening on net.Conn. Transform net.Conn to maConn After maListener accept inbound connection.
// maListener implements Listener
type maListener struct {
net.Listener
laddr ma.Multiaddr
}
// Accept waits for and returns the next connection to the listener.
// Returns a Multiaddr friendly Conn
func (l *maListener) Accept() (Conn, error) {
nconn, err := l.Listener.Accept()
if err != nil {
return nil, err
}
var raddr ma.Multiaddr
// This block protects us in transports (i.e. unix sockets) that don't have
// remote addresses for inbound connections.
if nconn.RemoteAddr().String() != "" {
raddr, err = FromNetAddr(nconn.RemoteAddr())
if err != nil {
return nil, fmt.Errorf("failed to convert conn.RemoteAddr: %s", err)
}
}
return wrap(nconn, l.laddr, raddr), nil
}
connection transform
maConn
multi addr conn is wrapped respectively via conn type. for example:
// Conn is the equivalent of a net.Conn object. It is the
// result of calling the Dial or Listen functions in this
// package, with associated local and remote Multiaddrs.
type Conn interface {
net.Conn
// LocalMultiaddr returns the local Multiaddr associated
// with this connection
LocalMultiaddr() ma.Multiaddr
// RemoteMultiaddr returns the remote Multiaddr associated
// with this connection
RemoteMultiaddr() ma.Multiaddr
}
implements:
// TCP
struct {
*net.TCPConn
maEndpoints
}
// UDP
struct {
*net.UDPConn
maEndpoints
}
// UNIX
struct {
*net.UnixConn
maEndpoints
}
CapableConn
CapableConn will be added to swarm.
// A CapableConn represents a connection that has offers the basic
// capabilities required by libp2p: stream multiplexing, encryption and
// peer authentication.
//
// These capabilities may be natively provided by the transport, or they
// may be shimmed via the "connection upgrade" process, which converts a
// "raw" network connection into one that supports such capabilities by
// layering an encryption channel and a stream multiplexer.
//
// CapableConn provides accessors for the local and remote multiaddrs used to
// establish the connection and an accessor for the underlying Transport.
type CapableConn interface {
mux.MuxedConn
network.ConnSecurity
network.ConnMultiaddrs
// Transport returns the transport to which this connection belongs.
Transport() Transport
}
implements
type transportConn struct {
mux.MuxedConn
network.ConnMultiaddrs
network.ConnSecurity
transport transport.Transport
}