github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/p2p/protocols/protocol.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:41</date> 10 //</624450105948246016> 11 12 13 /* 14 包协议是对p2p的扩展,它提供了一种用户友好的简单定义方法 15 通过抽象协议标准共享的代码来开发子协议。 16 17 *自动将代码索引分配给消息 18 *基于反射自动RLP解码/编码 19 *提供永久循环以读取传入消息 20 *标准化与通信相关的错误处理 21 *标准化握手谈判 22 *TODO:自动生成对等机的有线协议规范 23 24 **/ 25 26 package protocols 27 28 import ( 29 "bufio" 30 "bytes" 31 "context" 32 "fmt" 33 "io" 34 "reflect" 35 "sync" 36 "time" 37 38 "github.com/ethereum/go-ethereum/log" 39 "github.com/ethereum/go-ethereum/metrics" 40 "github.com/ethereum/go-ethereum/p2p" 41 "github.com/ethereum/go-ethereum/rlp" 42 "github.com/ethereum/go-ethereum/swarm/spancontext" 43 "github.com/ethereum/go-ethereum/swarm/tracing" 44 opentracing "github.com/opentracing/opentracing-go" 45 ) 46 47 //此协议方案使用的错误代码 48 const ( 49 ErrMsgTooLong = iota 50 ErrDecode 51 ErrWrite 52 ErrInvalidMsgCode 53 ErrInvalidMsgType 54 ErrHandshake 55 ErrNoHandler 56 ErrHandler 57 ) 58 59 //与代码关联的错误描述字符串 60 var errorToString = map[int]string{ 61 ErrMsgTooLong: "Message too long", 62 ErrDecode: "Invalid message (RLP error)", 63 ErrWrite: "Error sending message", 64 ErrInvalidMsgCode: "Invalid message code", 65 ErrInvalidMsgType: "Invalid message type", 66 ErrHandshake: "Handshake error", 67 ErrNoHandler: "No handler registered error", 68 ErrHandler: "Message handler error", 69 } 70 71 /* 72 错误实现标准Go错误接口。 73 用途: 74 75 错误F(代码、格式、参数…接口) 76 77 打印为: 78 79 <description>:<details> 80 81 其中由ErrorToString中的代码给出描述 82 详细信息是fmt.sprintf(格式,参数…) 83 84 可以检查导出的字段代码 85 **/ 86 87 type Error struct { 88 Code int 89 message string 90 format string 91 params []interface{} 92 } 93 94 func (e Error) Error() (message string) { 95 if len(e.message) == 0 { 96 name, ok := errorToString[e.Code] 97 if !ok { 98 panic("invalid message code") 99 } 100 e.message = name 101 if e.format != "" { 102 e.message += ": " + fmt.Sprintf(e.format, e.params...) 103 } 104 } 105 return e.message 106 } 107 108 func errorf(code int, format string, params ...interface{}) *Error { 109 return &Error{ 110 Code: code, 111 format: format, 112 params: params, 113 } 114 } 115 116 //wrappedmsg用于在消息有效负载旁边传播已封送的上下文 117 type WrappedMsg struct { 118 Context []byte 119 Size uint32 120 Payload []byte 121 } 122 123 //对于会计,设计是允许规范描述消息的定价方式和内容。 124 //为了访问这个功能,我们提供了一个钩子接口,它将调用会计方法 125 //注意:将来可能会有更多这样的(水平)钩子 126 type Hook interface { 127 //发送信息的钩子 128 Send(peer *Peer, size uint32, msg interface{}) error 129 //接收信息的钩子 130 Receive(peer *Peer, size uint32, msg interface{}) error 131 } 132 133 //规范是一种协议规范,包括其名称和版本以及 134 //交换的消息类型 135 type Spec struct { 136 //名称是协议的名称,通常是三个字母的单词 137 Name string 138 139 //version是协议的版本号 140 Version uint 141 142 //maxmsgsize是消息有效负载的最大可接受长度 143 MaxMsgSize uint32 144 145 //messages是此协议使用的消息数据类型的列表, 146 //发送的每个消息类型及其数组索引作为代码(因此 147 //[&foo,&bar,&baz]将发送带有代码的foo、bar和baz 148 //分别为0、1和2) 149 //每条消息必须有一个唯一的数据类型 150 Messages []interface{} 151 152 //会计挂钩(将来可扩展到多个挂钩) 153 Hook Hook 154 155 initOnce sync.Once 156 codes map[reflect.Type]uint64 157 types map[uint64]reflect.Type 158 } 159 160 func (s *Spec) init() { 161 s.initOnce.Do(func() { 162 s.codes = make(map[reflect.Type]uint64, len(s.Messages)) 163 s.types = make(map[uint64]reflect.Type, len(s.Messages)) 164 for i, msg := range s.Messages { 165 code := uint64(i) 166 typ := reflect.TypeOf(msg) 167 if typ.Kind() == reflect.Ptr { 168 typ = typ.Elem() 169 } 170 s.codes[typ] = code 171 s.types[code] = typ 172 } 173 }) 174 } 175 176 //length返回协议中的消息类型数 177 func (s *Spec) Length() uint64 { 178 return uint64(len(s.Messages)) 179 } 180 181 //getcode返回一个类型的消息代码,Boolean第二个参数是 182 //如果未找到消息类型,则为false 183 func (s *Spec) GetCode(msg interface{}) (uint64, bool) { 184 s.init() 185 typ := reflect.TypeOf(msg) 186 if typ.Kind() == reflect.Ptr { 187 typ = typ.Elem() 188 } 189 code, ok := s.codes[typ] 190 return code, ok 191 } 192 193 //newmsg构造给定代码的新消息类型 194 func (s *Spec) NewMsg(code uint64) (interface{}, bool) { 195 s.init() 196 typ, ok := s.types[code] 197 if !ok { 198 return nil, false 199 } 200 return reflect.New(typ).Interface(), true 201 } 202 203 //对等机表示在与的对等连接上运行的远程对等机或协议实例 204 //远程对等体 205 type Peer struct { 206 *p2p.Peer //代表远程的p2p.peer对象 207 rw p2p.MsgReadWriter //p2p.msgreadwriter,用于向发送消息和从中读取消息 208 spec *Spec 209 } 210 211 //new peer构造新的peer 212 //此构造函数由p2p.protocol run函数调用 213 //前两个参数是传递给p2p.protocol.run函数的参数 214 //第三个参数是描述协议的规范 215 func NewPeer(p *p2p.Peer, rw p2p.MsgReadWriter, spec *Spec) *Peer { 216 return &Peer{ 217 Peer: p, 218 rw: rw, 219 spec: spec, 220 } 221 } 222 223 //运行启动处理传入消息的Forever循环 224 //在p2p.protocol run函数中调用 225 //handler参数是为接收到的每个消息调用的函数。 226 //从远程对等机返回的错误导致循环退出 227 //导致断开 228 func (p *Peer) Run(handler func(ctx context.Context, msg interface{}) error) error { 229 for { 230 if err := p.handleIncoming(handler); err != nil { 231 if err != io.EOF { 232 metrics.GetOrRegisterCounter("peer.handleincoming.error", nil).Inc(1) 233 log.Error("peer.handleIncoming", "err", err) 234 } 235 236 return err 237 } 238 } 239 } 240 241 //DROP断开对等机的连接。 242 //TODO:可能只需要实现协议删除?不想把同伴踢开 243 //如果它们对其他协议有用 244 func (p *Peer) Drop(err error) { 245 p.Disconnect(p2p.DiscSubprotocolError) 246 } 247 248 //send接收一条消息,将其编码为rlp,找到正确的消息代码并发送 249 //向对等端发送消息 250 //这个低级调用将由提供路由或广播发送的库包装。 251 //但通常只用于转发和将消息推送到直接连接的对等端 252 func (p *Peer) Send(ctx context.Context, msg interface{}) error { 253 defer metrics.GetOrRegisterResettingTimer("peer.send_t", nil).UpdateSince(time.Now()) 254 metrics.GetOrRegisterCounter("peer.send", nil).Inc(1) 255 256 var b bytes.Buffer 257 if tracing.Enabled { 258 writer := bufio.NewWriter(&b) 259 260 tracer := opentracing.GlobalTracer() 261 262 sctx := spancontext.FromContext(ctx) 263 264 if sctx != nil { 265 err := tracer.Inject( 266 sctx, 267 opentracing.Binary, 268 writer) 269 if err != nil { 270 return err 271 } 272 } 273 274 writer.Flush() 275 } 276 277 r, err := rlp.EncodeToBytes(msg) 278 if err != nil { 279 return err 280 } 281 282 wmsg := WrappedMsg{ 283 Context: b.Bytes(), 284 Size: uint32(len(r)), 285 Payload: r, 286 } 287 288 //如果设置了会计挂钩,请调用它 289 if p.spec.Hook != nil { 290 err := p.spec.Hook.Send(p, wmsg.Size, msg) 291 if err != nil { 292 p.Drop(err) 293 return err 294 } 295 } 296 297 code, found := p.spec.GetCode(msg) 298 if !found { 299 return errorf(ErrInvalidMsgType, "%v", code) 300 } 301 return p2p.Send(p.rw, code, wmsg) 302 } 303 304 //手工编码(代码) 305 //在发送传入消息的主永久循环的每个循环中调用 306 //如果返回错误,则循环将返回,并且对等端将与错误断开连接。 307 //此通用处理程序 308 //*检查邮件大小, 309 //*检查超出范围的消息代码, 310 //*处理带反射的解码, 311 //*作为回调的调用处理程序 312 func (p *Peer) handleIncoming(handle func(ctx context.Context, msg interface{}) error) error { 313 msg, err := p.rw.ReadMsg() 314 if err != nil { 315 return err 316 } 317 //确保有效载荷已被完全消耗。 318 defer msg.Discard() 319 320 if msg.Size > p.spec.MaxMsgSize { 321 return errorf(ErrMsgTooLong, "%v > %v", msg.Size, p.spec.MaxMsgSize) 322 } 323 324 //取消标记包装的邮件,其中可能包含上下文 325 var wmsg WrappedMsg 326 err = msg.Decode(&wmsg) 327 if err != nil { 328 log.Error(err.Error()) 329 return err 330 } 331 332 ctx := context.Background() 333 334 //如果启用了跟踪,并且请求中的上下文是 335 //不是空的,试着解开它 336 if tracing.Enabled && len(wmsg.Context) > 0 { 337 var sctx opentracing.SpanContext 338 339 tracer := opentracing.GlobalTracer() 340 sctx, err = tracer.Extract( 341 opentracing.Binary, 342 bytes.NewReader(wmsg.Context)) 343 if err != nil { 344 log.Error(err.Error()) 345 return err 346 } 347 348 ctx = spancontext.WithContext(ctx, sctx) 349 } 350 351 val, ok := p.spec.NewMsg(msg.Code) 352 if !ok { 353 return errorf(ErrInvalidMsgCode, "%v", msg.Code) 354 } 355 if err := rlp.DecodeBytes(wmsg.Payload, val); err != nil { 356 return errorf(ErrDecode, "<= %v: %v", msg, err) 357 } 358 359 //如果设置了会计挂钩,请调用它 360 if p.spec.Hook != nil { 361 err := p.spec.Hook.Receive(p, wmsg.Size, val) 362 if err != nil { 363 return err 364 } 365 } 366 367 //调用已注册的处理程序回调 368 //注册的回调将解码后的消息作为参数作为接口 369 //应该将处理程序强制转换为适当的类型 370 //不检查处理程序中的强制转换是完全安全的,因为处理程序是 371 //首先根据正确的类型选择 372 if err := handle(ctx, val); err != nil { 373 return errorf(ErrHandler, "(msg code %v): %v", msg.Code, err) 374 } 375 return nil 376 } 377 378 //握手在对等连接上协商握手 379 //*参数 380 //*上下文 381 //*要发送到远程对等机的本地握手 382 //*在远程握手时要调用的函数(可以为零) 383 //*需要相同类型的远程握手 384 //*拨号对等端需要先发送握手,然后等待远程 385 //*侦听对等机等待远程握手,然后发送它 386 //返回远程握手和错误 387 func (p *Peer) Handshake(ctx context.Context, hs interface{}, verify func(interface{}) error) (rhs interface{}, err error) { 388 if _, ok := p.spec.GetCode(hs); !ok { 389 return nil, errorf(ErrHandshake, "unknown handshake message type: %T", hs) 390 } 391 errc := make(chan error, 2) 392 handle := func(ctx context.Context, msg interface{}) error { 393 rhs = msg 394 if verify != nil { 395 return verify(rhs) 396 } 397 return nil 398 } 399 send := func() { errc <- p.Send(ctx, hs) } 400 receive := func() { errc <- p.handleIncoming(handle) } 401 402 go func() { 403 if p.Inbound() { 404 receive() 405 send() 406 } else { 407 send() 408 receive() 409 } 410 }() 411 412 for i := 0; i < 2; i++ { 413 select { 414 case err = <-errc: 415 case <-ctx.Done(): 416 err = ctx.Err() 417 } 418 if err != nil { 419 return nil, errorf(ErrHandshake, err.Error()) 420 } 421 } 422 return rhs, nil 423 } 424