go libp2p listen

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

conn transform flow

net.Conn multiaddr Conn CapableConn wrap upgrade net.Conn multiaddr Conn CapableConn
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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值