ping-pong.go 4.7 KB

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