github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/p2p/simulations/examples/ping-pong.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package main 13 14 import ( 15 "flag" 16 "fmt" 17 "io/ioutil" 18 "net/http" 19 "os" 20 "sync/atomic" 21 "time" 22 23 "github.com/Sberex/go-sberex/log" 24 "github.com/Sberex/go-sberex/node" 25 "github.com/Sberex/go-sberex/p2p" 26 "github.com/Sberex/go-sberex/p2p/discover" 27 "github.com/Sberex/go-sberex/p2p/simulations" 28 "github.com/Sberex/go-sberex/p2p/simulations/adapters" 29 "github.com/Sberex/go-sberex/rpc" 30 ) 31 32 var adapterType = flag.String("adapter", "sim", `node adapter to use (one of "sim", "exec" or "docker")`) 33 34 // main() starts a simulation network which contains nodes running a simple 35 // ping-pong protocol 36 func main() { 37 flag.Parse() 38 39 // set the log level to Trace 40 log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) 41 42 // register a single ping-pong service 43 services := map[string]adapters.ServiceFunc{ 44 "ping-pong": func(ctx *adapters.ServiceContext) (node.Service, error) { 45 return newPingPongService(ctx.Config.ID), nil 46 }, 47 } 48 adapters.RegisterServices(services) 49 50 // create the NodeAdapter 51 var adapter adapters.NodeAdapter 52 53 switch *adapterType { 54 55 case "sim": 56 log.Info("using sim adapter") 57 adapter = adapters.NewSimAdapter(services) 58 59 case "exec": 60 tmpdir, err := ioutil.TempDir("", "p2p-example") 61 if err != nil { 62 log.Crit("error creating temp dir", "err", err) 63 } 64 defer os.RemoveAll(tmpdir) 65 log.Info("using exec adapter", "tmpdir", tmpdir) 66 adapter = adapters.NewExecAdapter(tmpdir) 67 68 case "docker": 69 log.Info("using docker adapter") 70 var err error 71 adapter, err = adapters.NewDockerAdapter() 72 if err != nil { 73 log.Crit("error creating docker adapter", "err", err) 74 } 75 76 default: 77 log.Crit(fmt.Sprintf("unknown node adapter %q", *adapterType)) 78 } 79 80 // start the HTTP API 81 log.Info("starting simulation server on 0.0.0.0:8888...") 82 network := simulations.NewNetwork(adapter, &simulations.NetworkConfig{ 83 DefaultService: "ping-pong", 84 }) 85 if err := http.ListenAndServe(":8888", simulations.NewServer(network)); err != nil { 86 log.Crit("error starting simulation server", "err", err) 87 } 88 } 89 90 // pingPongService runs a ping-pong protocol between nodes where each node 91 // sends a ping to all its connected peers every 10s and receives a pong in 92 // return 93 type pingPongService struct { 94 id discover.NodeID 95 log log.Logger 96 received int64 97 } 98 99 func newPingPongService(id discover.NodeID) *pingPongService { 100 return &pingPongService{ 101 id: id, 102 log: log.New("node.id", id), 103 } 104 } 105 106 func (p *pingPongService) Protocols() []p2p.Protocol { 107 return []p2p.Protocol{{ 108 Name: "ping-pong", 109 Version: 1, 110 Length: 2, 111 Run: p.Run, 112 NodeInfo: p.Info, 113 }} 114 } 115 116 func (p *pingPongService) APIs() []rpc.API { 117 return nil 118 } 119 120 func (p *pingPongService) Start(server *p2p.Server) error { 121 p.log.Info("ping-pong service starting") 122 return nil 123 } 124 125 func (p *pingPongService) Stop() error { 126 p.log.Info("ping-pong service stopping") 127 return nil 128 } 129 130 func (p *pingPongService) Info() interface{} { 131 return struct { 132 Received int64 `json:"received"` 133 }{ 134 atomic.LoadInt64(&p.received), 135 } 136 } 137 138 const ( 139 pingMsgCode = iota 140 pongMsgCode 141 ) 142 143 // Run implements the ping-pong protocol which sends ping messages to the peer 144 // at 10s intervals, and responds to pings with pong messages. 145 func (p *pingPongService) Run(peer *p2p.Peer, rw p2p.MsgReadWriter) error { 146 log := p.log.New("peer.id", peer.ID()) 147 148 errC := make(chan error) 149 go func() { 150 for range time.Tick(10 * time.Second) { 151 log.Info("sending ping") 152 if err := p2p.Send(rw, pingMsgCode, "PING"); err != nil { 153 errC <- err 154 return 155 } 156 } 157 }() 158 go func() { 159 for { 160 msg, err := rw.ReadMsg() 161 if err != nil { 162 errC <- err 163 return 164 } 165 payload, err := ioutil.ReadAll(msg.Payload) 166 if err != nil { 167 errC <- err 168 return 169 } 170 log.Info("received message", "msg.code", msg.Code, "msg.payload", string(payload)) 171 atomic.AddInt64(&p.received, 1) 172 if msg.Code == pingMsgCode { 173 log.Info("sending pong") 174 go p2p.Send(rw, pongMsgCode, "PONG") 175 } 176 } 177 }() 178 return <-errC 179 }