github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/p2p/simulations/examples/ping-pong.go (about)

     1  //  Copyright 2018 The go-ethereum Authors
     2  //  Copyright 2019 The go-aigar Authors
     3  //  This file is part of the go-aigar library.
     4  //
     5  //  The go-aigar library is free software: you can redistribute it and/or modify
     6  //  it under the terms of the GNU Lesser General Public License as published by
     7  //  the Free Software Foundation, either version 3 of the License, or
     8  //  (at your option) any later version.
     9  //
    10  //  The go-aigar library is distributed in the hope that it will be useful,
    11  //  but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  //  GNU Lesser General Public License for more details.
    14  //
    15  //  You should have received a copy of the GNU Lesser General Public License
    16  //  along with the go-aigar library. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package main
    19  
    20  import (
    21  	"flag"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"net/http"
    25  	"os"
    26  	"sync/atomic"
    27  	"time"
    28  
    29  	"github.com/AigarNetwork/aigar/log"
    30  	"github.com/AigarNetwork/aigar/node"
    31  	"github.com/AigarNetwork/aigar/p2p"
    32  	"github.com/AigarNetwork/aigar/p2p/enode"
    33  	"github.com/AigarNetwork/aigar/p2p/simulations"
    34  	"github.com/AigarNetwork/aigar/p2p/simulations/adapters"
    35  	"github.com/AigarNetwork/aigar/rpc"
    36  )
    37  
    38  var adapterType = flag.String("adapter", "sim", `node adapter to use (one of "sim", "exec" or "docker")`)
    39  
    40  // main() starts a simulation network which contains nodes running a simple
    41  // ping-pong protocol
    42  func main() {
    43  	flag.Parse()
    44  
    45  	// set the log level to Trace
    46  	log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
    47  
    48  	// register a single ping-pong service
    49  	services := map[string]adapters.ServiceFunc{
    50  		"ping-pong": func(ctx *adapters.ServiceContext) (node.Service, error) {
    51  			return newPingPongService(ctx.Config.ID), nil
    52  		},
    53  	}
    54  	adapters.RegisterServices(services)
    55  
    56  	// create the NodeAdapter
    57  	var adapter adapters.NodeAdapter
    58  
    59  	switch *adapterType {
    60  
    61  	case "sim":
    62  		log.Info("using sim adapter")
    63  		adapter = adapters.NewSimAdapter(services)
    64  
    65  	case "exec":
    66  		tmpdir, err := ioutil.TempDir("", "p2p-example")
    67  		if err != nil {
    68  			log.Crit("error creating temp dir", "err", err)
    69  		}
    70  		defer os.RemoveAll(tmpdir)
    71  		log.Info("using exec adapter", "tmpdir", tmpdir)
    72  		adapter = adapters.NewExecAdapter(tmpdir)
    73  
    74  	default:
    75  		log.Crit(fmt.Sprintf("unknown node adapter %q", *adapterType))
    76  	}
    77  
    78  	// start the HTTP API
    79  	log.Info("starting simulation server on 0.0.0.0:8888...")
    80  	network := simulations.NewNetwork(adapter, &simulations.NetworkConfig{
    81  		DefaultService: "ping-pong",
    82  	})
    83  	if err := http.ListenAndServe(":8888", simulations.NewServer(network)); err != nil {
    84  		log.Crit("error starting simulation server", "err", err)
    85  	}
    86  }
    87  
    88  // pingPongService runs a ping-pong protocol between nodes where each node
    89  // sends a ping to all its connected peers every 10s and receives a pong in
    90  // return
    91  type pingPongService struct {
    92  	id       enode.ID
    93  	log      log.Logger
    94  	received int64
    95  }
    96  
    97  func newPingPongService(id enode.ID) *pingPongService {
    98  	return &pingPongService{
    99  		id:  id,
   100  		log: log.New("node.id", id),
   101  	}
   102  }
   103  
   104  func (p *pingPongService) Protocols() []p2p.Protocol {
   105  	return []p2p.Protocol{{
   106  		Name:     "ping-pong",
   107  		Version:  1,
   108  		Length:   2,
   109  		Run:      p.Run,
   110  		NodeInfo: p.Info,
   111  	}}
   112  }
   113  
   114  func (p *pingPongService) APIs() []rpc.API {
   115  	return nil
   116  }
   117  
   118  func (p *pingPongService) Start(server *p2p.Server) error {
   119  	p.log.Info("ping-pong service starting")
   120  	return nil
   121  }
   122  
   123  func (p *pingPongService) Stop() error {
   124  	p.log.Info("ping-pong service stopping")
   125  	return nil
   126  }
   127  
   128  func (p *pingPongService) Info() interface{} {
   129  	return struct {
   130  		Received int64 `json:"received"`
   131  	}{
   132  		atomic.LoadInt64(&p.received),
   133  	}
   134  }
   135  
   136  const (
   137  	pingMsgCode = iota
   138  	pongMsgCode
   139  )
   140  
   141  // Run implements the ping-pong protocol which sends ping messages to the peer
   142  // at 10s intervals, and responds to pings with pong messages.
   143  func (p *pingPongService) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error {
   144  	log := p.log.New("peer.id", peer.ID())
   145  
   146  	errC := make(chan error)
   147  	go func() {
   148  		for range time.Tick(10 * time.Second) {
   149  			log.Info("sending ping")
   150  			if err := p2p.Send(rw, pingMsgCode, "PING"); err != nil {
   151  				errC <- err
   152  				return
   153  			}
   154  		}
   155  	}()
   156  	go func() {
   157  		for {
   158  			msg, err := rw.ReadMsg()
   159  			if err != nil {
   160  				errC <- err
   161  				return
   162  			}
   163  			payload, err := ioutil.ReadAll(msg.Payload)
   164  			if err != nil {
   165  				errC <- err
   166  				return
   167  			}
   168  			log.Info("received message", "msg.code", msg.Code, "msg.payload", string(payload))
   169  			atomic.AddInt64(&p.received, 1)
   170  			if msg.Code == pingMsgCode {
   171  				log.Info("sending pong")
   172  				go p2p.Send(rw, pongMsgCode, "PONG")
   173  			}
   174  		}
   175  	}()
   176  	return <-errC
   177  }