// called every "tickTime" see: createBilateral
called every "tickTime" see: createBilateral
// called every " tickTime " see : createBilateral
func (twoway *Bilateral) sender() (err error) {
	t := time.Now()
	for e := twoway.timeoutQueue.Front(); nil != e; e = e.Prev() {
		qi := e.Value.(queueItem)
		if qi.timestamp.After(t) {
			break
		}
		if request, ok := twoway.rpcReturns[qi.id]; ok {
			twoway.rpcClientResponseChannel <- rpcClientTimeoutData{
				id: request.id,
			}
		}
		twoway.timeoutQueue.Remove(e)
	}
loop:
	for to, c := range twoway.connections {
		if c.timestamp.After(time.Now()) {
			continue loop
		}
		switch c.state {
		case stateStopped:
		case stateMakeServer:
			log.Infof("make disconnect: %q (%q)", c.address, to)
			err = twoway.outgoingSocket.Disconnect(c.address)
			if nil != err {
				log.Errorf("disconnect(MakeServer) error: %v", err)
			}
			c.timestamp = time.Now().Add(SERVE_TIME)
			c.state = stateServing
		case stateDestroy:
			log.Infof("destroy disconnect: %q (%q)", c.address, to)
			twoway.outgoingSocket.Disconnect(c.address)
			if nil != err {
				log.Errorf("disconnect(Destroy) error: %v", err)
			}
			c.state = stateStopped
			delete(twoway.connections, to)
		case stateServing:
			log.Info("serving timed out")
			c.state = stateStopped
		case stateWaiting:
			if twoway.encrypted {
				twoway.outgoingSocket.SetCurveServerkey(c.name)
			}
			log.Infof("waiting connect: %q @ %q", to, c.address)
			err := twoway.outgoingSocket.Connect(c.address)
			if nil != err {
				log.Errorf("connect error: %v", err)
				c.state = stateStopped
			} else {
				c.state = stateHandshake
				c.timestamp = time.Now().Add(FIRST_JOIN_TIME)
				c.joinCount = 0
			}
		case stateHandshake:
			if c.joinCount < MAXIMUM_JOINS {
				c.joinCount += 1
				c.timestamp = time.Now().Add(JOIN_TIME)
				log.Infof("join %d → %q", c.joinCount, to)
				err = sendPacket(twoway.outgoingSocket, to, "JOIN", []byte(twoway.networkName))
			} else {
				log.Infof("handshake disconnect: %q (%q)", c.address, to)
				twoway.outgoingSocket.Disconnect(c.address)
				if nil != err {
					log.Errorf("disconnect(Handshake) error: %v", err)
				}
				c.state = stateWaiting
				c.timestamp = time.Now().Add(RETRY_TIME << c.backoff)
				if c.backoff < MAXIMUM_BACKOFF {
					c.backoff += 1
				}
			}
		case stateConnected:
			c.tick += 1
			log.Infof("tick %d → %q", c.tick, to)
			tickBuffer := make([]byte, 8)
			binary.BigEndian.PutUint64(tickBuffer, c.tick)
			err = sendPacket(twoway.outgoingSocket, to, "TICK", tickBuffer)
			c.timestamp = time.Now().Add(LIVE_TIME)
			c.state = stateCheck
		case stateCheck:
			log.Infof("check disconnect: %q (%q)", c.address, to)
			err := twoway.outgoingSocket.Disconnect(c.address)
			if nil != err {
				log.Errorf("disconnect(Check) error: %v", err)
			}
			c.state = stateWaiting
			c.timestamp = time.Now().Add(RETRY_TIME << c.backoff)
			if c.backoff < MAXIMUM_BACKOFF {
				c.backoff += 1
			}
		}
	}
	return nil
}
func ( twoway * Bilateral ) sender ( ) ( err error ) { t := time . Now ( ) for e := twoway . timeoutQueue . Front ( ) ; nil != e ; e = e . Prev ( ) { qi := e . Value . ( queueItem ) if qi . timestamp . After ( t ) { break } if request , ok := twoway . rpcReturns [ qi . id ] ; ok { twoway . rpcClientResponseChannel <- rpcClientTimeoutData { id : request . id , } } twoway . timeoutQueue . Remove ( e ) } loop : for to , c := range twoway . connections { if c . timestamp . After ( time . Now ( ) ) { continue loop } switch c . state { case stateStopped : case stateMakeServer : log . Infof ( " " , c . address , to ) err = twoway . outgoingSocket . Disconnect ( c . address ) if nil != err { log . Errorf ( " " , err ) } c . timestamp = time . Now ( ) . Add ( SERVE_TIME ) c . state = stateServing case stateDestroy : log . Infof ( " " , c . address , to ) twoway . outgoingSocket . Disconnect ( c . address ) if nil != err { log . Errorf ( " " , err ) } c . state = stateStopped delete ( twoway . connections , to ) case stateServing : log . Info ( " " ) c . state = stateStopped case stateWaiting : if twoway . encrypted { twoway . outgoingSocket . SetCurveServerkey ( c . name ) } log . Infof ( " " , to , c . address ) err := twoway . outgoingSocket . Connect ( c . address ) if nil != err { log . Errorf ( " " , err ) c . state = stateStopped } else { c . state = stateHandshake c . timestamp = time . Now ( ) . Add ( FIRST_JOIN_TIME ) c . joinCount = 0 } case stateHandshake : if c . joinCount < MAXIMUM_JOINS { c . joinCount += 1 c . timestamp = time . Now ( ) . Add ( JOIN_TIME ) log . Infof ( " c j o inCount,  t ) err = sendPacket ( twoway . outgoingSocket , to , " " , [ ] byte ( twoway . networkName ) ) } else { log . Infof ( " " , c . address , to ) twoway . outgoingSocket . Disconnect ( c . address ) if nil != err { log . Errorf ( " " , err ) } c . state = stateWaiting c . timestamp = time . Now ( ) . Add ( RETRY_TIME << c . backoff ) if c . backoff < MAXIMUM_BACKOFF { c . backoff += 1 } } case stateConnected : c . tick += 1 log . Infof ( " c t i ck,  t ) tickBuffer := make ( [ ] byte , 8 ) binary . BigEndian . PutUint64 ( tickBuffer , c . tick ) err = sendPacket ( twoway . outgoingSocket , to , " " , tickBuffer ) c . timestamp = time . Now ( ) . Add ( LIVE_TIME ) c . state = stateCheck case stateCheck : log . Infof ( " " , c . address , to ) err := twoway . outgoingSocket . Disconnect ( c . address ) if nil != err { log . Errorf ( " " , err ) } c . state = stateWaiting c . timestamp = time . Now ( ) . Add ( RETRY_TIME << c . backoff ) if c . backoff < MAXIMUM_BACKOFF { c . backoff += 1 } } } return nil }
called every "tickTime" see: createBilateral
called every " tickTime " see : createBilateral
// create a timeout message // not time yet // only get here if timed out - no client request in SERVE_TIME // if encrypted set remote public key