github.com/Unheilbar/quorum@v1.0.0/p2p/simulations/examples/ping-pong.go (about) 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 17 package main 18 19 import ( 20 "flag" 21 "fmt" 22 "io/ioutil" 23 "net/http" 24 "os" 25 "sync/atomic" 26 "time" 27 28 "github.com/ethereum/go-ethereum/log" 29 "github.com/ethereum/go-ethereum/node" 30 "github.com/ethereum/go-ethereum/p2p" 31 "github.com/ethereum/go-ethereum/p2p/enode" 32 "github.com/ethereum/go-ethereum/p2p/simulations" 33 "github.com/ethereum/go-ethereum/p2p/simulations/adapters" 34 ) 35 36 var adapterType = flag.String("adapter", "sim", `node adapter to use (one of "sim", "exec" or "docker")`) 37 38 // main() starts a simulation network which contains nodes running a simple 39 // ping-pong protocol 40 func main() { 41 flag.Parse() 42 43 // set the log level to Trace 44 log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) 45 46 // register a single ping-pong service 47 services := map[string]adapters.LifecycleConstructor{ 48 "ping-pong": func(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) { 49 pps := newPingPongService(ctx.Config.ID) 50 stack.RegisterProtocols(pps.Protocols()) 51 return pps, nil 52 }, 53 } 54 adapters.RegisterLifecycles(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) Start() error { 115 p.log.Info("ping-pong service starting") 116 return nil 117 } 118 119 func (p *pingPongService) Stop() error { 120 p.log.Info("ping-pong service stopping") 121 return nil 122 } 123 124 func (p *pingPongService) Info() interface{} { 125 return struct { 126 Received int64 `json:"received"` 127 }{ 128 atomic.LoadInt64(&p.received), 129 } 130 } 131 132 const ( 133 pingMsgCode = iota 134 pongMsgCode 135 ) 136 137 // Run implements the ping-pong protocol which sends ping messages to the peer 138 // at 10s intervals, and responds to pings with pong messages. 139 func (p *pingPongService) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error { 140 log := p.log.New("peer.id", peer.ID()) 141 142 errC := make(chan error) 143 go func() { 144 for range time.Tick(10 * time.Second) { 145 log.Info("sending ping") 146 if err := p2p.Send(rw, pingMsgCode, "PING"); err != nil { 147 errC <- err 148 return 149 } 150 } 151 }() 152 go func() { 153 for { 154 msg, err := rw.ReadMsg() 155 if err != nil { 156 errC <- err 157 return 158 } 159 payload, err := ioutil.ReadAll(msg.Payload) 160 if err != nil { 161 errC <- err 162 return 163 } 164 log.Info("received message", "msg.code", msg.Code, "msg.payload", string(payload)) 165 atomic.AddInt64(&p.received, 1) 166 if msg.Code == pingMsgCode { 167 log.Info("sending pong") 168 go p2p.Send(rw, pongMsgCode, "PONG") 169 } 170 } 171 }() 172 return <-errC 173 }