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