github.com/aquanetwork/aquachain@v1.7.8/p2p/simulations/examples/ping-pong.go (about) 1 // Copyright 2017 The aquachain Authors 2 // This file is part of the aquachain library. 3 // 4 // The aquachain 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 aquachain 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 aquachain 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 "gitlab.com/aquachain/aquachain/common/log" 29 "gitlab.com/aquachain/aquachain/node" 30 "gitlab.com/aquachain/aquachain/p2p" 31 "gitlab.com/aquachain/aquachain/p2p/discover" 32 "gitlab.com/aquachain/aquachain/p2p/simulations" 33 "gitlab.com/aquachain/aquachain/p2p/simulations/adapters" 34 "gitlab.com/aquachain/aquachain/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 case "docker": 74 log.Info("using docker adapter") 75 var err error 76 adapter, err = adapters.NewDockerAdapter() 77 if err != nil { 78 log.Crit("error creating docker adapter", "err", err) 79 } 80 81 default: 82 log.Crit(fmt.Sprintf("unknown node adapter %q", *adapterType)) 83 } 84 85 // start the HTTP API 86 log.Info("starting simulation server on 0.0.0.0:8888...") 87 network := simulations.NewNetwork(adapter, &simulations.NetworkConfig{ 88 DefaultService: "ping-pong", 89 }) 90 if err := http.ListenAndServe(":8888", simulations.NewServer(network)); err != nil { 91 log.Crit("error starting simulation server", "err", err) 92 } 93 } 94 95 // pingPongService runs a ping-pong protocol between nodes where each node 96 // sends a ping to all its connected peers every 10s and receives a pong in 97 // return 98 type pingPongService struct { 99 id discover.NodeID 100 log log.Logger 101 received int64 102 } 103 104 func newPingPongService(id discover.NodeID) *pingPongService { 105 return &pingPongService{ 106 id: id, 107 log: log.New("node.id", id), 108 } 109 } 110 111 func (p *pingPongService) Protocols() []p2p.Protocol { 112 return []p2p.Protocol{{ 113 Name: "ping-pong", 114 Version: 1, 115 Length: 2, 116 Run: p.Run, 117 NodeInfo: p.Info, 118 }} 119 } 120 121 func (p *pingPongService) APIs() []rpc.API { 122 return nil 123 } 124 125 func (p *pingPongService) Start(server *p2p.Server) error { 126 p.log.Info("ping-pong service starting") 127 return nil 128 } 129 130 func (p *pingPongService) Stop() error { 131 p.log.Info("ping-pong service stopping") 132 return nil 133 } 134 135 func (p *pingPongService) Info() interface{} { 136 return struct { 137 Received int64 `json:"received"` 138 }{ 139 atomic.LoadInt64(&p.received), 140 } 141 } 142 143 const ( 144 pingMsgCode = iota 145 pongMsgCode 146 ) 147 148 // Run implements the ping-pong protocol which sends ping messages to the peer 149 // at 10s intervals, and responds to pings with pong messages. 150 func (p *pingPongService) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error { 151 log := p.log.New("peer.id", peer.ID()) 152 153 errC := make(chan error) 154 go func() { 155 for range time.Tick(10 * time.Second) { 156 log.Info("sending ping") 157 if err := p2p.Send(rw, pingMsgCode, "PING"); err != nil { 158 errC <- err 159 return 160 } 161 } 162 }() 163 go func() { 164 for { 165 msg, err := rw.ReadMsg() 166 if err != nil { 167 errC <- err 168 return 169 } 170 payload, err := ioutil.ReadAll(msg.Payload) 171 if err != nil { 172 errC <- err 173 return 174 } 175 log.Info("received message", "msg.code", msg.Code, "msg.payload", string(payload)) 176 atomic.AddInt64(&p.received, 1) 177 if msg.Code == pingMsgCode { 178 log.Info("sending pong") 179 go p2p.Send(rw, pongMsgCode, "PONG") 180 } 181 } 182 }() 183 return <-errC 184 }