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