github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/p2p/simulations/examples/ping-pong.go (about)

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