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